Shahzad Bhatti

April 23, 2014

Implementing Reactive Extensions (RX) using Java 8

Filed under: Java — admin @ 11:52 pm

In my last blog, I described new lambda support in Java 8. In order to try new Java features in more depth, I implemented Reactive extensions in Java 8. In short, reactive extensions allows processing synchronous and asynchronous in data uniform manner. It provides unified interfaces that can be used as an iterator or callback method for asynchronous processing. Though, Microsoft RX library is huge but I only implemented core features and focused on Observable API. Here is brief overview of implementation:

Creating Observable from Collection

Here is how you can create Observable from a collection:

    List<String> names = Arrays.asList("One", "Two", "Three", "Four", "Five"); 
    Observable.from(names).subscribe(System.out::println, 
       Throwable::printStackTrace, () -> System.out.println("done"));
 

In Microsoft’s version of RX, Observable takes an Observer for subscription, which defines three methods: onNext, onError and onCompleted. onNext is invoked to push next element of data, onError is used to notify errors and onCompleted is called when data is all processed. In my implementation, the Observable interface defines two overloaded subscribe method, first takes callback functions for onNext and onError and second method takes three callback functions including onCompleted. I chose to use separate function parameters instead of a single interface so that caller can pass inline lambda functions instead of passing implementation of Observer interface.

Creating Observable from Array of objects

Here is how you can create Observable from stream:

    Observable.from("Erica", "Matt", "John", "Mike").subscribe(System.out::println, 
         Throwable::printStackTrace, () -> System.out.println("done"));
 

Creating Observable from Stream

Here is how you can create Observable from stream:

    Stream<String> names = Stream.of("One", "Two", "Three", "Four", "Five"); 
    // note third argument for onComplete is optional
    Observable.from(names).subscribe(name -> System.out.println(name), 
       error -> error.printStackTrace());
 

Creating Observable from Iterator

Here is how you can create Observable from iterator:

    Stream<String> names = Stream.of("One", "Two", "Three", "Four", "Five"); 
    Observable.from(names.iterator()).subscribe(name -> System.out.println(name), 
       error -> error.printStackTrace());
 

Creating Observable from Spliterator

Here is how you can create Observable from spliterator:

    List<String> names = Arrays.asList("One", "Two", "Three", "Four", "Five"); 
    Observable.from(names.spliterator()).subscribe(System.out::println, 
       Throwable::printStackTrace);
 

Creating Observable from a single object

Here is how you can create Observable from a single object:

    Observable.just("value").subscribe(v -> System.out.println(v), 
       error -> error.printStackTrace());
    // if a single object is collection, it would be treated as a single entity, e.g.
    Observable.just(Arrays.asList(1, 2, 3)).subscribe( num -> System.out.println(num), 
       error -> error.printStackTrace());
 

Creating Observable for an error

Here is how you can create Observable that would return an error:

    Observable.throwing(new Error("test error")).subscribe(System.out::println, 
       error -> System.err.println(error));
    // this will print error 
 

Creating Observable from a consumer function

Here is how you can create Observable that takes user function for invoking onNext, onError and onCompleted function:

    Observable.create(observer -> {
       for (String name : names) {
          observer.onNext(name);
       }
       observer.onCompleted();
    }).subscribe(System.out::println, Throwable::printStackTrace);
 

Creating Observable from range

Here is how you can create Observable from stream that would create numbers from start to end range exclusively.

    // Creates range of numbers starting at from until it reaches to exclusively
    Observable.range(4, 8).subscribe(num -> System.out.println(num), 
       error -> error.printStackTrace());
    // will print 4, 5, 6, 7
 

Creating empty Observable

It would call onCompleted right away:

    Observable.empty().subscribe(System.out::println, 
       Throwable::printStackTrace, () -> System.out.println("Completed"));
 

Creating never Observable

It would not call any of call back methods:

    Observable.never().subscribe(System.out::println, Throwable::printStackTrace);
 
 

Changing Scheduler

By default Observable notifies observer asynchronously using thread-pool scheduler but you can change default scheduler as follows:

Using thread-pool scheduler

    Stream<String> names = Stream.of("One", "Two", "Three", "Four", "Five"); 
    Observable.from(names).subscribeOn(Scheduler.getThreadPoolScheduler()).
       subscribe(System.out::println, Throwable::printStackTrace);
 

Using new-thread scheduler

It will create new thread

    Stream<String> names = Stream.of("One", "Two", "Three", "Four", "Five"); 
    Observable.from(names).subscribeOn(Scheduler.getNewThreadScheduler()).
       subscribe(System.out::println, Throwable::printStackTrace);
 

Using timer thread with interval

It will notify at each interval

    Stream<String> names = Stream.of("One", "Two", "Three", "Four", "Five"); 
    Observable.from(names).subscribeOn(Scheduler.getTimerSchedulerWithMilliInterval(1000)).
       subscribe(System.out::println, Throwable::printStackTrace);
    // this will print each name every second
 

Using immediate scheduler

This scheduler calls callback functions right away on the same thread. You can use this if you synchronous data and don’t want to create another thread. On the downside, you cannot unsubscribe with this scheduler.

    Stream<String> names = Stream.of("One", "Two", "Three", "Four", "Five"); 
    Observable.from(names).subscribeOn(Scheduler.getImmediateScheduler()).
       subscribe(System.out::println, Throwable::printStackTrace);
 

Transforming

Observables keep sequence of items as streams and they support map/flatMap operation as supported by standard Stream class, e.g.

Map

    Stream<String> names = Stream.of("One", "Two", "Three", "Four", "Five"); 
    Observable.from(names).map(name -> name.hashCode()).
       subscribe(System.out::println, Throwable::printStackTrace);
 

FlatMap

    Stream integerListStream = Stream.of( Arrays.asList(1, 2), 
       Arrays.asList(3, 4), Arrays.asList(5));
    Observable.from(integerListStream).flatMap(integerList -> integerList.stream()).
       subscribe(System.out::println, Throwable::printStackTrace);
 

Filtering

Observables supports basic filtering support as provided by Java Streams, e.g.

Filter

    Stream<String> names = Stream.of("One", "Two", "Three", "Four", 
       "Five"); 
    Observable.from(names).filter(name -> name.startsWith("T")).
       subscribe(System.out::println, Throwable::printStackTrace);
    // This will only print Two and Three
 

Skip

skips given number of elements

    Stream<String> names = Stream.of("One", "Two", "Three", "Four", "Five"); 
    Observable.from(names).skip(2).subscribe(System.out::println, 
       Throwable::printStackTrace);
    // This will skip One and Two
 

Limit

    Stream<String> names = Stream.of("One", "Two", "Three", "Four", "Five"); 
    Observable.from(names).limit(2).subscribe(System.out::println, 
       Throwable::printStackTrace);
    // This will only print first two strings
 

Distinct

    Stream<String> names = Stream.of("One", "Two", "Three", "One");
    Observable.from(names).distinct.subscribe(System.out::println, 
       Throwable::printStackTrace);
    // This will print One only once
 

Merge

This concates two observable data:

    Observable<Integer> observable2 = Observable.from(Stream.of(4, 5, 6));
    observable1.merge(observable2).subscribe(System.out::println, 
       Throwable::printStackTrace);
    // This will print 1, 2, 3, 4, 5, 6
 

Summary

In summary, as Java 8 already supported a lot of functional primitives, adding support for reactive extensions was quite straight forward. For example, Nextflix’s implementation of reactive extensions in Java consists of over 80K lines of code but it took few hundred lines to implement core features with Java 8. You can download or fork the code from https://github.com/bhatti/RxJava8.


April 17, 2014

Introduction to Java 8 Lambda and Stream Syntax

Filed under: Java — admin @ 11:10 pm

Introduction

Java 8 was released in March 2014 with most language-level enhancements since Java 5 back in 2004. The biggest new feature is introduction to Lambda. Lambda or Closur is a block of code that you can pass to other methods or return from methods. Previously, Java supported a form of closure via anonymous class syntax, e.g.

 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.Dimension;
 import java.awt.FlowLayout;
 import javax.swing.JButton;
 import javax.swing.JFrame;
 
 public class SwingExample extends JFrame {
   public SwingExample() {
     this.getContentPane().setLayout(new FlowLayout());
     final JButton btn = new JButton("Click Me");
     btn.setPreferredSize(new Dimension(400,200));
     add(btn);
     btn.addActionListener(new ActionListener() {
       public void actionPerformed(ActionEvent e) {
         btn.setText("Clicked");
         btn.setEnabled(false);
       }
     });
   }
 
   private static void createAndShowGUI() {
     JFrame frame = new SwingExample();
     frame.pack();
     frame.setVisible(true);
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
   }
 
   public static void main(String[] args) {
     javax.swing.SwingUtilities.invokeLater(new Runnable() {
       public void run() {
         createAndShowGUI(); 
       }
     });
   }
 }
 

In above example, using anonymous class could use locally defined data in method that declares it as long as it is defined with final. Here is how the example looks like with Java 8 syntax:

 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.Dimension;
 import java.awt.FlowLayout;
 import javax.swing.JButton;
 import javax.swing.JFrame;
 
 public class SwingExample8 extends JFrame {
   public SwingExample8() {
     this.getContentPane().setLayout(new FlowLayout());
     JButton btn = new JButton("Click Me");
     btn.setPreferredSize(new Dimension(400,200));
     add(btn);
     btn.addActionListener(e -> {
         btn.setText("Clicked");
         btn.setEnabled(false);
       }
     );
   }
 
   private static void createAndShowGUI() {
     JFrame frame = new SwingExample8();
     frame.pack();
     frame.setVisible(true);
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
   }
 
   public static void main(String[] args) {
     javax.swing.SwingUtilities.invokeLater(new Runnable() {
       public void run() {
         createAndShowGUI(); 
       }
     });
   }
 }
 

As you can see, lambda syntax is very minimal. In addition, lambda syntax doesn’t require that you declare externally accessible data as final, though it cannot be changed. Java lambda also adds type inferencing so that you don’t have to define types of arguments. The lambda features are implemented using “invokedynamic” instruction to dispatch method calls, which was added in Java 7 to support dynamic languages. For example, let’s take a simple example:

 public class Run8 {
   public static void main(String[] args) {
     Runnable r = () -> System.out.println("hello there");
     r.run();
   }
 }
 

If you decompile it using

 javap -p Run8
 

You will see, it generated lambda$main$0 method, e.g.

 public class Run8 {
   public Run8();
   public static void main(java.lang.String[]);
   private static void lambda$main$0();
 }
 

You can see real byte code using

 javap -p -c Run8
 

and you will see new byte codes:

 public class Run8 {
   public Run8();
     Code:
        0: aload_0       
        1: invokespecial #1                  // Method java/lang/Object
        4: return        
 
   public static void main(java.lang.String[]);
     Code:
        0: invokedynamic #2,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
        5: astore_1      
        6: aload_1       
        7: invokeinterface #3,  1            // InterfaceMethod java/lang/Runnable.run:()V
       12: return        
 
   private static void lambda$main$0();
     Code:
        0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        3: ldc           #5                  // String hello there
        5: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        8: return        
 }
 

This means lambdas don’t have to keep reference of enclosing class and “this” inside lambda does not create new scope.

Types of Functions

Java 8 provides predefined functions (See http://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html), but there are four major types:

                 Supplier: () -> T
                 Consumer: T -> ()
                 Predicate: T -> boolean
                 Function: T -> R
 

The supplier method takes not arguments and produces an object, the consumer takes an argument for consumption, predicate evaluates given argument by returning true/false and function maps an argument of type T and returns an object of type R.

Supplier example

 import java.util.function.*;
 
 public class Supply8 {
   public static void main(String[] args) {
     Supplier<Double>> random1 = Math::random;
     System.out.println(random1.get());
     //
     DoubleSupplier random2 = Math::random;
     System.out.println(random2.getAsDouble());
   }
 }
 

Note: Java 8 provides special functions for primitive types that you can use instead of using wrapper classes for primitive types. Here is another example that shows how you can write efficient log messages:

 import java.util.function.*;
 
 public class SupplyLog {
   private static boolean debugEnabled; 
   public static void debug(Supplier<String> msg) {
     if (debugEnabled) {
       System.out.println(msg.get());
     }
   }
   public static void main(String[] args) {
     debug(() -> "this will not be printed");
     debugEnabled = true;
     debug(() -> "this will be printed");
   }
 }
 

Consumer example

 import java.util.function.*;
 
 public class Consume8 {
   public static void main(String[] args) {
     Consumer<String> consumer = s -> System.out.println(s);
     consumer.accept("hello there");
     consumer.andThen(consumer).accept("this will be printed twice");
   }
 }
 

Predicate example

 import java.util.function.*;
 
 public class Predicate8 {
   public static void main(String[] args) {
     Predicate<Integer> gradeA = score -> score >= 90;
     System.out.println(gradeA.test(80));
     System.out.println(gradeA.test(90));
   }
 }
 

In addition to test method, you can also use and, negate, or method to combine other predicates.

Function example

 import java.util.function.*;
 
 public class Function8 {
   public static void main(String[] args) {
     BinaryOperator<Integer> adder = (n1, n2) -> n1 + n2;
     System.out.println("sum " + adder.apply(4, 5));
     Function<Double>,Double> square = x -> x * x;
     System.out.println("square " + square.apply(5.0));
   }
 }
 

Custom Functions

In addition to predefined functions, you can define your own interface for functions as long as there is a single method is declared. You can optionally declare interface with @FunctionalInterface annotation so that compiler can verify it, e.g.

 public class CustomFunction8 {
   @FunctionalInterface
   interface Command<T> {
     void execute(T obj);
   }
 
   private static <T> void invoke(Command<T> cmd, T arg) {
     cmd.execute(arg);
   }
 
 
   public static void main(String[] args) {
     Command<Integer> cmd = arg -> System.out.println(arg);
     invoke(cmd, 5);
   }
 }
 

Method Reference

In addition to passing lambda, you can also pass instance or static methods as closures using method reference. There are four kinds of method references:

  • Reference to a static method ContainingClass::staticMethodName
  • Reference to an instance method of a particular object ContainingObject::instanceMethodName
  • Reference to an instance method of an arbitrary object of a particular type ContainingType::methodName
  • Reference to a constructor ClassName::new

Streams

In addition to lambda support, Java 8 has updated collection classes to support streams. Streams don’t really store anything but they behave as pipes for computation lazily. Though, collections have limited size, but streams can be unlimited and they can be only consumed once. Streams can be accessed from collections using stream() and parallelStream() methods or from an array via Arrays.stream(Object[]). There are also static factory methods on the stream classes, such as Stream.of(Object[]), IntStream.range(int, int), etc. Common intermediate methods using as pipes that you can invoke on streams:

  • filter()
  • distinct()
  • limit()
  • map()
  • peek()
  • sorted()
  • unsorted()

In above examples sorted, distinct, unsorted are stateful, whereas filter, map, limit are stateless. And here are terminal operations that trigger evaluation on streams:

  • findFirst()
  • min()
  • max()
  • reduce()
  • sum()

You can implement your own streams by using helper methods in StreamSupport class.

Iterating

Java 8 streams support forEach method for iterating, which can optionally take a consumer function, e.g.

 import java.util.stream.Stream;
 
 public class StreamForEach {
   public static void main(String[] args) {
     Stream<String> symbols = Stream.of("AAPL", "MSFT", "ORCL", "NFLX", "TSLA");
     symbols.forEach(System.out::println);
   }
 }
 

Parallel iteration:

 import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Stream;
 
 public class ParStreamForEach {
   public static void main(String[] args) {
     List<String> symbols = Arrays.asList("AAPL", "MSFT", "ORCL", "NFLX", "TSLA");
     System.out.println("unordered");
     symbols.parallelStream().forEach(System.out::println);
     System.out.println("ordered");
     symbols.parallelStream().forEachOrdered(System.out::println);
   }
 }
 

Note that by default iterating parallel stream would be unordered but you can force ordered iteration using forEachOrdered method instead of forEach.

Filtering

We already saw predicate functions and filtering support in streams allow extract elements of collections that evaluates true to given predicate. Let’s create a couple of classes that we will use later:

 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.function.IntPredicate;
 
 
 public class Game implements IntPredicate {
   enum Type {
     AGE_ALL,
     AGE_13_OR_ABOVE,
     AGE_18_OR_ABOVE
   }
 
   public final String name;
   public final Type type;
   public final Collection<Player> players = new ArrayList<>();
 
 
   public Game(String name, Type type) {
     this.name = name;
     this.type = type;
   }
 
   public boolean suitableForAll() {
     return type == Type.AGE_ALL;
   }
   public void add(Player player) {
     if (test(player.age)) {
       this.players.add(player);
     }
   }
 
   @Override
   public boolean test(int age) {
     switch (type) {
       case AGE_18_OR_ABOVE:
         return age >= 18;
       case AGE_13_OR_ABOVE:
         return age >= 13;
       default:
         return true;
     }
   }
   @Override 
   public String toString() {
     return name;
   }
 }
 
 public class Player {
   public final String name;
   public final int age;
   public Player(String name, int age) {
     this.name = name;
     this.age = age;
   }
   @Override 
   public String toString() {
     return name;
   }
 }
 

Now let’s create a class that will filter games by types:

 import java.util.Arrays;
 import java.util.Collection;
 import static java.util.stream.Collectors.*;
 import java.util.stream.Stream;
 
 
 public class GameFilter {
   public static void main(String[] args) {
     Collection<Game>> games = Arrays.asList(new Game("Birdie", Game.Type.AGE_ALL), new Game("Draw", Game.Type.AGE_ALL), new Game("Poker", Game.Type.AGE_18_OR_ABOVE), new Game("Torpedo", Game.Type.AGE_13_OR_ABOVE));
     Collection<Game>> suitableForAll = games.stream().filter(Game::suitableForAll).collect(toList());
     System.out.println("suitable for all");
     suitableForAll.stream().forEach(System.out::println);
     Collection<Game>> adultOnly = games.stream().filter(game -> game.type == Game.Type.AGE_18_OR_ABOVE).limit(10).collect(toList());
     System.out.println("suitable for adults only");
     adultOnly.stream().forEach(System.out::println);
   }
 }
 

As you can see, filter can accept lambda or method reference.

Map

Map operation on streams applies a given function to transform each element in stream and produces another stream with transformed elements.

 import java.util.Arrays;
 import java.util.Collection;
 import static java.util.stream.Collectors.*;
 import java.util.stream.Stream;
 
 
 public class GameMap {
   public static void main(String[] args) {
     Collection<Game>> games = Arrays.asList(new Game("Birdie", Game.Type.AGE_ALL), new Game("Draw", Game.Type.AGE_ALL), new Game("Poker", Game.Type.AGE_18_OR_ABOVE), new Game("Torpedo", Game.Type.AGE_13_OR_ABOVE));
     Collection<Player> players = Arrays.asList(new Player("John", 10), new Player("David", 15), new Player("Matt", 20), new Player("Dan", 30), new Player("Erica", 5));
     for (Game game : games) {
       for (Player player : players) {
         game.add(player);
       }
     }
     //
     Collection<Game>.Type> types = games.stream().map(game -> game.type).collect(toList());
     System.out.println("types:");
     types.stream().forEach(System.out::println);
     Collection<Player> allPlayers = games.stream().flatMap(game -> game.players.stream()).collect(toList());
     System.out.println("\nplayers:");
     players.stream().forEach(System.out::println);
   }
 }
 
 

Note that flatMap takes collection of objects for each input and flattens it and produces a single collection. Java streams also produces map methods for primitive types such as mapToLong, mapToDouble, etc.

Sorting

Previously, you had to implement Comparable interface or provide Comparator for sorting but you can now pass lambda for comparison, e.g.

 import java.util.Arrays;
 import java.util.List;
 import static java.util.stream.Collectors.*;
 import java.util.stream.Stream;
 import java.util.Comparator;
 
 
 public class GameSort {
   public static void main(String[] args) {
     List<Player> players = Arrays.asList(new Player("John", 10), new Player("David", 15), new Player("Matt", 20), new Player("Dan", 30), new Player("Erica", 5));
     players.sort(Comparator.comparing(player -> player.age));
     System.out.println(players);
   }
 }
 

Min/Max

Java streams provide helper methods for calculating min/max, e.g.

 import java.util.Arrays;
 import java.util.Collection;
 import static java.util.stream.Collectors.*;
 import java.util.stream.Stream;
 import java.util.Comparator;
 
 
 public class GameMinMax {
   public static void main(String[] args) {
     Collection<Player> players = Arrays.asList(new Player("John", 10), new Player("David", 15), new Player("Matt", 20), new Player("Dan", 30), new Player("Erica", 5));
     Player min = players.stream().min(Comparator.comparing(player -> player.age)).get();
     Player max = players.stream().max(Comparator.comparing(player -> player.age)).get();
     System.out.println("min " + min + ", max " + max);
   }
 }
 
 

Reduce/Fold

Reduce or fold generalizes the problem where we compuate a single value from collection, e.g.

 import java.util.Arrays;
 import java.util.Collection;
 import static java.util.stream.Collectors.*;
 import java.util.stream.Stream;
 
 
 public class GameReduce {
   public static void main(String[] args) {
     Collection<Player> players = Arrays.asList(new Player("John", 10), new Player("David", 15), new Player("Matt", 20), new Player("Dan", 30), new Player("Erica", 5));
     double averageAge1 = players.stream().mapToInt(player -> player.age).average().getAsDouble();
     double averageAge2 = players.stream().mapToInt(player -> player.age).reduce(0, Integer::sum) / players.size();
     double averageAge3 = players.stream().mapToInt(player -> player.age).reduce(0, (sum, age) -> sum + age) / players.size();
     System.out.println("average age " + averageAge1 + ", " + averageAge2 + ", or " + averageAge3);
   }
 }
 

Grouping/Partitioning

groupingBy method of Collectors allows grouping collection, e.g.

 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import static java.util.stream.Collectors.*;
 import java.util.stream.Stream;
 
 
 public class GameGrouping {
   public static void main(String[] args) {
     Collection<Player> players = Arrays.asList(new Player("John", 10), new Player("David", 15), new Player("Matt", 20), new Player("Dan", 30), new Player("Erica", 5));
     Map<Integer, List<Player>> playersByAge = players.stream().collect(groupingBy(player -> player.age));
     System.out.println(playersByAge);
   }
 }
 

partitioningBy groups collection into two collection, e.g.

 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import static java.util.stream.Collectors.*;
 import java.util.stream.Stream;
 
 
 public class GamePartition {
   public static void main(String[] args) {
     Collection<Player> players = Arrays.asList(new Player("John", 10), new Player("David", 15), new Player("Matt", 20), new Player("Dan", 30), new Player("Erica", 5));
     Map<Boolean, List<Player>> playersByAge = players.stream().collect(groupingBy(player -> player.age >= 18));
     System.out.println(playersByAge);
   }
 }
 

String joining

Here is an example of creating a string from collection:

 import static java.util.stream.Collectors.*;
 import java.util.stream.Stream;
 
 
 public class GameJoin {
   public static void main(String[] args) {
     Stream<Player> players = Stream.of(new Player("John", 10), new Player("David", 15), new Player("Matt", 20), new Player("Dan", 30), new Player("Erica", 5));
     System.out.println(players.map(Player::toString).collect(joining(",", "[", "]")));
   }
 }
 

Lazy Evaluation

Java Map interface now supports lazy evaluation by adding object to Map if the key is not present:

 import java.util.HashMap;
 import java.util.Map;
 
 public class Fib {
   private final static Map<Integer,Long> cache = new HashMap<Integer, Long>() {{
     put(0,0L);
     put(1,1L);
   }};
   public static long fib(int x) {
     return cache.computeIfAbsent(x, n -> fib(n-1) + fib(n-2));
   }
 
   public static void main(String[] args) {
     System.out.println(fib(10));
   }
 }
 

Parallel Streams

Though, streams processes elements serially but you can change stream() method of collection to parallelStream to take advantage of parallel processing of stream. Parallel stream use ForkJoinPool by default and use as many threads as you have processors (Runtime.getRuntime().availableProcessors()), e.g.

 import java.util.Arrays;
 import java.util.List;
 import java.util.stream.IntStream;
 import java.util.stream.Stream;
 
 public class ParStreamPrime {
   private static boolean isPrime(int n) {
     if (n%2==0) return false;
     for(int i=3;i*i<=n;i+=2) {
       if(n%i==0) return false;
     }
     return true;
   }
   private static long serialTest(int max) {
     long started = System.currentTimeMillis();
     IntStream.rangeClosed(1, max).forEach(num -> isPrime(num));
     return System.currentTimeMillis() - started;
   }
   private static long parallelTest(int max) {
     long started = System.currentTimeMillis();
     IntStream.rangeClosed(1, max).parallel().forEach(num -> isPrime(num));
     return System.currentTimeMillis() - started;
   }
   //
   public static void main(String[] args) {
     int max = 1000000;
     System.out.println("Serial " + serialTest(max));
     System.out.println("Parallel " + parallelTest(max));
   }
 }
 

If you need to customize thread pool size, you can create parallel stream inside fork-join-pool, e.g.

   private static void parallelTest(final int max) {
     ForkJoinPool forkJoinPool = new ForkJoinPool(2);
     forkJoinPool.submit(() ->
        IntStream.rangeClosed(1, max).parallel().forEach(num -> isPrime(num));
     ).get();
   }
 

Default methods and Mixins

For anyone who has to support interfaces for multiple clients knows the frustration of adding new methods because it requires updating all clients. You can now add default methods on interfaces and add static methods, e.g.

 interface Vehicle {
   float getMaxSpeedMPH();
   public static String getType(Vehicle v) {
     return v.getClass().getSimpleName();
   }
 }
 
 interface Car extends Vehicle {
   void drive();
   public default float getMaxSpeedMPH() {
     return 200;
   }
 }
 interface Boat extends Vehicle {
   void row();
   public default float getMaxSpeedMPH() {
     return 100;
   }
 }
 
 interface Plane extends Vehicle {
   void fly();
   public default float getMaxSpeedMPH() {
     return 500;
   }
 }
 
 public class AmphiFlyCar implements Car, Boat, Plane {
   @Override
   public void drive() {
     System.out.println("drive");
   }
   @Override
   public void row() {
     System.out.println("row");
   }
   @Override
   public void fly() {
     System.out.println("fly");
   }
   public float getMaxSpeedMPH() {
     return Plane.super.getMaxSpeedMPH();
   }
   public static void main(String[] args) {
     AmphiFlyCar v = new AmphiFlyCar();
     System.out.println(Vehicle.getType(v) + ": " + v.getMaxSpeedMPH());
   }
 }
 

Optional

One of biggest bane of Java applications is NullPointerException and you can get rid of those using Optional, which acts as Maybe monads in other languages, e.g.

 import java.util.HashMap;
 import java.util.Map;
 import static java.util.stream.Collectors.*;
 import java.util.stream.Stream;
 import java.util.Optional;
 
 
 public class OptionalExample {
   private static Map<String, Player> players = new HashMap<String, Player>() {{
     put("John", new Player("John", 10));
     put("David", new Player("David", 15));
     put("Matt", new Player("Matt", 20));
     put("Erica", new Player("Erica", 25));
   }};
 
   private static Optional<Player> findPlayerByName(String name) {
     Player player = players.get(name);
     return player == null ? Optional.empty() : Optional.of(player);
   }
 
   private static Integer getAge(Player player) {
     return player.age;
   }
 
 
   public static void main(String[] args) {
     findPlayerByName("John").ifPresent(System.out::println);
     Player player = findPlayerByName("Jeff").orElse(new Player("Jeff", 40));
     System.out.println("orElse " + player);
     Integer age = findPlayerByName("Jeff").map(OptionalExample::getAge).orElse(-1);
     System.out.println("Jeff age " + age);
   }
 }
 

CompletableFuture

Java has long supported future, but previously you had to call blocking get() to retrieve the result. With Java 8, you can use CompletableFuture to define the behavior when asynchronous processing is completed, e.g.

 private static final ExecutorService executor = Executors.newFixedThreadPool(2);
 public static CompletableFuture getPlayer(String name) {
    return CompletableFuture.supplyAsync(() -> new Player(), executor);
 }
 getQuote("name").thenAccept(player -> System.out.println(player));
 
 

Summary

Over the past few years, functional programming languages have become mainstream and Java 8 brings many of those capabilites despite being late. These new features help write better and more concise code. You will have to change existing code to make more use of immutable objects and use streams instead of objects when possible. As far as stability, I found java 8 compiler on Linux environment a bit buggy that crashed often so it may take a little while before Java 8 can be used in production.


September 22, 2010

An implementation of Virtual Node Router with Consistent Hash algorithm

Filed under: Java — admin @ 1:02 pm

Since the Dynamo paper, published a few years ago, DHTs and consistent hash have become mainstream. Here is my implementation of a virtual node router that uses consistent hash algorithm for splitting requests to the virtual nodes:

 import java.nio.ByteBuffer;
 import java.security.MessageDigest;
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.SortedMap;
 import java.util.TreeMap;
 
 public class VirtualNodeRouter {
     interface HashCalculator {
         long calculateHash(String key);
     }
 
     private static class VirtualNode {
         final String nodeName;
         final int replicaNumber;
 
         VirtualNode(final String nodeName, final int replicaNumber) {
             this.nodeName = nodeName.toLowerCase();
             this.replicaNumber = replicaNumber;
         }
 
         boolean matches(String host) {
             return nodeName.equalsIgnoreCase(host);
         }
 
         @Override
         public String toString() {
             return nodeName + ":" + replicaNumber;
         }
     }
 
     private final HashCalculator hashFunction;
     private final SortedMap<Long, VirtualNode> virtualNodePoolByHash = new TreeMap<Long, VirtualNode>(
             new Comparator<Long>() {
                 public int compare(Long i, Long j) {
                     if (i > j) {
                         return 1;
                     } else if (i < j) {
                         return -1;
                     } else {
                         return 0;
                     }
                 }
             });
 
     public VirtualNodeRouter() {
         this(new HashCalculator() {
             public long calculateHash(String key) {
                 try {
                     MessageDigest sha1 = MessageDigest.getInstance("SHA1");
                     sha1.update(key.getBytes());
                     byte[] digest = sha1.digest();
                     return bytesToLong(digest);
                 } catch (Exception e) {
                     throw new RuntimeException(e);
                 }
             }
         });
     }
 
     public VirtualNodeRouter(final HashCalculator f) {
         this.hashFunction = f;
     }
 
     /**
      * Adds a node with one replica
      * 
      * @param node
      *            - node name
      */
     public void add(String node) {
         add(node, 1);
     }
 
     /**
      * Adds a node to the available pool
      * 
      * @param node
      *            - node name
      * @param replicas
      *            - # of replicas - increase # of replicas based on the
      *            computing power of the machine
      */
     public void add(String node, int replicas) {
         // Note: You can call this method incrementally by adding more replicas,
         // so that you don't cause DOS on
         // your own services
         int existingReplicas = getReplicas(node);
 
         for (int i = 0; i < replicas; i++) {
             VirtualNode virtualNode = new VirtualNode(node, i
                     + existingReplicas);
             virtualNodePoolByHash.put(hashFunction.calculateHash(virtualNode
                     .toString()), virtualNode);
         }
     }
 
     /**
      * remove the node from available pool
      * 
      * @param node
      */
     public void remove(String node) {
         Iterator<Long> it = virtualNodePoolByHash.keySet().iterator();
         while (it.hasNext()) {
             Long key = it.next();
             VirtualNode virtualNode = virtualNodePoolByHash.get(key);
             if (virtualNode.matches(node)) {
                 it.remove();
             }
         }
     }
 
     public String getNode(String key) {
         if (virtualNodePoolByHash.isEmpty()) {
             return null;
         }
         long hash = hashFunction.calculateHash(key);
         for (Map.Entry<Long, VirtualNode> e : virtualNodePoolByHash.entrySet()) {
             if (hash < e.getKey()) {
                 return e.getValue().nodeName;
             }
         }
         SortedMap<Long, VirtualNode> tailMap = virtualNodePoolByHash
                 .tailMap(hash);
         hash = tailMap.isEmpty() ? virtualNodePoolByHash.firstKey() : tailMap
                 .firstKey();
         return virtualNodePoolByHash.get(hash).nodeName;
     }
 
     public void dump() {
         for (Map.Entry<Long, VirtualNode> e : virtualNodePoolByHash.entrySet()) {
             System.out.println("  " + e.getKey() + " => " + e.getValue());
         }
     }
 
     public int getReplicas(String nodeName) {
         int replicas = 0;
         for (VirtualNode node : virtualNodePoolByHash.values()) {
             if (node.matches(nodeName)) {
                 replicas++;
             }
         }
         return replicas;
     }
 
     private static long bytesToLong(byte[] b) {
         ByteBuffer bb = ByteBuffer.wrap(b);
         return bb.getLong();
     }
 }
 

The virtual nodes are added by specifying the node name and the number of replicas. You can add more virtual nodes for the more powerful machines than the low-end machines. You can call remove method when the node goes down or is unavailable. The getNode is called when a request needs to be routed. For example, if you are caching results of a service, you can use the key of cache to find the virtual node and then store the cache value at that node. You can read following resources to learn more about the consistent hash algorithm:



November 16, 2009

Applying Adaptive Object Model using dynamic languages and schema-less databases

Filed under: Java — admin @ 3:10 pm

Introduction to Adaptive/Active Object Model

Adaptive or Active Object Model is a design pattern used in domains that requires dynamic manipulation of meta information.
Though, it is quite extensive topic of research, but general idea from original paper of
Ralph Johnson is to treat meta information such as attributes,
rules and relationships as a data. It is usually used when the number of sub-classes is huge or unknown upfront and the system requires adding new functionality without downtime.
For example, let’s say we are working in automobile domain and we need to model different type of vehicles. Using an object oriented design would result in vehicle hierarchy such as follows:

In above example, all type hierarchy is predefined and each class within the hierarchy defines attributes and operations. Adaptive Object Modeling on the other hand use Object Type pattern, which treats classes like objects. The basic Adaptive Object Model uses type square model such as:

In above diagram, EntityType class represents all classes and instance of this class defines actual attributes and operations supported by the class. Similarly, PropertyType defines names and types of all attributes. Finally, instance of Entity class will actual be real object instance that would store collection of properties and would refer to the EntityType.

Java Implementation

Let’s assume we only need to model Vehicle class from above vehicle hierarchy. In a typical object oriented language such as Java, the Vehicle class would be defined as follows:

  1 /*
  2  * Simple Vehicle class
  3  * 
  4  */
 
  5 package com.plexobject.aom;
  6 
  7 import java.util.Date;
  8 
 
  9 public class Vehicle {
 10 
 11     private String maker;
 12     private String model;
 
 13     private Date yearCreated;
 14     private double speed;
 15     private long miles;
 
 16     //... other attributes, accessors, setters
 17 
 18     public void drive() {
 19         //
 
 20     }
 21 
 22     public void stop() {
 23         //
 
 24     }
 25 
 26     public void performMaintenance() {
 27         //
 28     }
 
 29     //... other methods
 30 }
 31 
 32 
 33 
 

As you can see all attributes and operations are defined within the Vehicle class. The Adaptive Object Model would use meta classes such as Entity, EntityType, Property and PropertyType to build the Vehicle metaclass. Following Java code defines core classes of type square model:

The Property class defines type and value for each attribute of class:

  1 /*
  2  * Property class defines attribute type and value
  3  * 
  4  */
 
  5 package com.plexobject.aom;
  6 
  7 public class Property {
 
  8 
  9     private PropertyType propertyType;
 10     private Object value;
 11 
 12     public Property(PropertyType propertyType, Object value) {
 
 13         this.propertyType = propertyType;
 14         this.value = value;
 15     }
 16 
 17     public PropertyType getPropertyType() {
 
 18         return propertyType;
 19     }
 20 
 21     public Object getValue() {
 22         return value;
 
 23     }
 24     //... other methods
 25 }
 26 
 27 
 

The PropertyType class defines type information for each attribute of class:

  1 /*
  2  * PropertyType class defines type information
  3  * 
  4  */
 
  5 package com.plexobject.aom;
 
  6 
  7 public class PropertyType {
  8 
  9     private String propertyName;
 
 10     private String type;
 11 
 12     public PropertyType(String propertyName, String type) {
 13         this.propertyName = propertyName;
 14         this.type = type;
 
 15     }
 16 
 17     public String getPropertyName() {
 18         return propertyName;
 19     }
 
 20 
 21     public String getType() {
 22         return type;
 23     }
 24     //... other methods
 
 25 }

The EntityType class defines type of entity:

  1 /*
  2  * EntityType class defines attribute types and operations
  3  * 
  4  */
  5 package com.plexobject.aom;
 
  6 
  7 import java.util.Collection;
  8 import java.util.HashMap;
  9 import java.util.Map;
 
 10 
 11 public class EntityType {
 12 
 13     private String typeName;
 14     private Map<String, PropertyType> propertyTypes = new HashMap<String, PropertyType>();
 
 15     private Map<String, Operation> operations = new HashMap<String, Operation>();
 16 
 17     public EntityType(String typeName) {
 
 18         this.typeName = typeName;
 19     }
 20 
 21     public String getTypeName() {
 22         return typeName;
 
 23     }
 24 
 25     public void addPropertyType(PropertyType propertyType) {
 26         propertyTypes.put(propertyType.getPropertyName(),
 27                 propertyType);
 
 28     }
 29 
 30     public Collection<PropertyType> getPropertyTypes() {
 31         return propertyTypes.values();
 
 32     }
 33 
 34     public PropertyType getPropertyType(String propertyName) {
 35         return propertyTypes.get(propertyName);
 36     }
 
 37 
 38     public void addOperation(String operationName, Operation operation) {
 39         operations.put(operationName, operation);
 40 
 41     }
 
 42 
 43     public Operation getOperation(String name) {
 44         return operations.get(name);
 45     }
 46 
 
 47     public Collection<Operation> getOperations() {
 48         return operations.values();
 49     }
 50     //... other methods
 
 51 }
 52 
 53 
 

The Entity class defines entity itself:

  1 /*
  2  * Entity class represents instance of actual metaclass
  3  * 
  4  */
  5 package com.plexobject.aom;
 
  6 
  7 import java.util.Collection;
  8 import java.util.Collections;
  9 
 
 10 public class Entity {
 11 
 12     private EntityType entityType;
 13     private Collection<Property> properties;
 
 14 
 15     public Entity(EntityType entityType) {
 16         this.entityType = entityType;
 17     }
 18 
 19     public EntityType getEntityType() {
 
 20         return entityType;
 21     }
 22 
 23     public void addProperty(Property property) {
 
 24         properties.add(property);
 25     }
 26 
 27     public Collection<Property> getProperties() {
 28         return Collections.unmodifiableCollection(properties);
 
 29     }
 30 
 31     public Object perform(String operationName, Object[] args) {
 32         return entityType.getOperation(operationName).perform(this, args);
 
 33     }
 34     //... other methods
 35 }

The Operation interface is used for implementing behavior using Command pattern:

  1 /*
  2  * Operation interface defines behavior
  3  * 
  4  */
  5 package com.plexobject.aom;
 
  6 
  7 public interface Operation {
  8 
  9     Object perform(Entity entity, Object[] args);
 
 10 }

Above meta classes would be used to create classes and objects. For example, the type information of Vehicle class would be defined in EntityType and PropertyType and the instance would be defined using Entity and Property classes as follows. Though, in real applications, type binding would be stored in XML configuration or will be defined in some DSL, but I am binding programmatically below:

  1 /*
  2  * an example of binding attributes and operations of Vehicle
  3  * 
  4  */
 
  5 package com.plexobject.aom;
  6 
  7 import java.util.Date;
  8 
 
  9 
 10 public class Initializer {
 11 
 12     public void bind() {
 
 13         EntityType vehicleType = new EntityType("Vehicle");
 14         vehicleType.addPropertyType(new PropertyType("maker",
 15                 "java.lang.String"));
 
 16         vehicleType.addPropertyType(new PropertyType("model",
 17                 "java.lang.String"));
 18         vehicleType.addPropertyType(new PropertyType("yearCreated",
 
 19                 "java.util.Date"));
 20         vehicleType.addPropertyType(new PropertyType("speed",
 21                 "java.lang.Double"));
 22         vehicleType.addPropertyType(new PropertyType("miles",
 
 23                 "java.lang.Long"));
 24         vehicleType.addOperation("drive", new Operation() {
 25 
 26             public Object perform(Entity entity, Object[] args) {
 
 27                 return "driving";
 28             }
 29         });
 30         vehicleType.addOperation("stop", new Operation() {
 
 31 
 32             public Object perform(Entity entity, Object[] args) {
 33                 return "stoping";
 34             }
 35         });
 
 36         vehicleType.addOperation("performMaintenance", new VehicleMaintenanceOperation());
 37 
 38 
 39         // now creating instance of Vehicle
 40         Entity vehicle = new Entity(vehicleType);
 
 41         vehicle.addProperty(new Property(vehicleType.getPropertyType("maker"),
 42                 "Toyota"));
 43         vehicle.addProperty(new Property(vehicleType.getPropertyType("model"),
 
 44                 "Highlander"));
 45         vehicle.addProperty(new Property(vehicleType.getPropertyType("yearCreated"),
 46                 new Date(2003, 0, 1)));
 
 47         vehicle.addProperty(new Property(vehicleType.getPropertyType("speed"), new Double(120)));
 48         vehicle.addProperty(new Property(vehicleType.getPropertyType("miles"), new Long(3000)));
 
 49         vehicle.perform(
 50                 "drive", null);
 51 
 52     }
 53 }
 
 54 
 55 
 

The operations define runtime behavior of the class and can be defined as closures (anonymous classes) or external implementation such as VehicleMaintenanceOperation as follows:

  1 /*
 
  2  * an example of operation
  3  * 
  4  */
 
  5 package com.plexobject.aom;
  6 
  7 class VehicleMaintenanceOperation implements Operation {
 
  8 
  9     public VehicleMaintenanceOperation() {
 10     }
 11 
 12     public Object perform(Entity entity, Object[] args) {
 
 13         return "maintenance";
 14     }
 15 }
 16 
 17 
 
 

In real applications, you would also have meta classes for business rules, relationships, strategies, validations, etc as instances. As, you can see AOM provides powerful way to adopt new business requirements and I have seen it used successfully while working as consultant. On the downside, it requires a lot of plumbing and tooling support such as XML based configurations or GUI tools to manipulate meta data. I have also found it difficult to optimize with relational databases as each attribute and operation are stored in separate rows in the databases, which results in excessive joins when building the object. There are a number of alternatives of Adaptive Object Model such as code generators, generative techniques, metamodeling, and table-driven systems. These techniques are much easier with dynamic languages due to their support of metaprogramming, higher order functions and generative programming. Also, over the last few years, a number of schema less databases such as CouchDB, MongoDB, Redis, Cassendra, Tokyo Cabinet, Riak, etc. have become popular due to their ease of use and scalability. These new databases solve excessive join limitation of relational databases and allow evolution of applications similar to Adaptive Object Model. They are also much more scalable than traditional databases. The combination of dynamic languages and schema less databases provides a simple way to add Adaptive Object Model features without a lot of plumbing code.

Javascript Implementation

Let’s try above example in Javascript due to its supports of higher order functions, and prototype based inheritance capabilities. First, we will need to add some helper methods to Javascript (adopted from Douglas Crockford’s “Javascript: The Good Parts”), e.g.

  1 
  2 if (typeof Object.beget !== 'function') {
 
  3     Object.beget = function(o) {
  4         var F = function() {};
  5         F.prototype = o;
 
  6         return new F();
  7     }
  8 }
  9 
 
 10 Function.prototype.method = function (name, func) {
 11     this.prototype[name] = func;
 12     return this;
 13 };
 
 14 
 15 
 16 Function.method('new', function() {
 17     // creating new object that inherits from constructor's prototype
 
 18     var that = Object.beget(this.prototype);
 19     // invoke the constructor, binding -this- to new object
 
 20     var other = this.apply(that, arguments);
 21     // if its return value isn't an object substitute the new object
 
 22     return (typeof other === 'object' && other) || that;
 23 });
 24 
 
 25 Function.method('inherits', function(Parent) {
 26     this.prototype = new Parent();
 27     return this;
 
 28 });
 29 
 30 Function.method('bind', function(that) {
 31     var method = this;
 
 32     var slice = Array.prototype.slice;
 33     var args = slice.apply(arguments, [1]);
 34     return function() {
 35         return method.apply(that, args.concat(slice.apply(arguments,
 
 36             [0])));
 37     };
 38 });
 39 
 40 // as typeof is broken in Javascript, trying to get type from the constructor
 
 41 Object.prototype.typeName = function() {
 42     return typeof(this) === 'object' ? this.constructor.toString().split(/[\s\(]/)[1] : typeof(this);
 
 43 };
 44 
 45 
 

There is no need to define Operation interface, Property and PropertyType due to higher order function and dynamic language support. Following Javascript code defines core functionality of Entity and EntityType classes, e.g.:

  1 
  2 var EntityType = function(typeName, propertyNamesAndTypes) {
 
  3     this.typeName = typeName;
  4     this.propertyNamesAndTypes = propertyNamesAndTypes;
  5     this.getPropertyTypesAndNames = function() {
 
  6         return this.propertyNamesAndTypes;
  7     };
  8     this.getPropertyType = function(propertyName) {
 
  9         return this.propertyNamesAndTypes[propertyName];
 10     };
 11     this.getTypeName = function() {
 12         return this.typeName;
 
 13     };
 14     var that = this;
 15     for (propertyTypesAndName in propertyNamesAndTypes) {
 
 16         that[propertyTypesAndName] = function(name) {
 17             return function() {
 18                 return propertyNamesAndTypes[name];
 
 19             };
 20         }(propertyTypesAndName);
 21         
 22     }
 
 23 };
 24 
 25 
 26 
 27 var Entity = function(entityType, properties) {
 28     this.entityType = entityType;
 
 29     this.properties = properties;
 30     this.getEntityType = function() {
 31         return this.entityType;
 32     };
 
 33     var that = this;
 34     for (propertyTypesAndName in entityType.getPropertyTypesAndNames()) {
 35         that[propertyTypesAndName] = function(name) {
 
 36             return function() {
 37                 if (arguments.length == 0) {
 38                     return that.properties[name];
 39                 } else {
 
 40                     var oldValue = that.properties[name];
 41                     that.properties[name] = arguments[0];
 42                     return oldValue;
 43                 }
 44             };
 
 45         }(propertyTypesAndName);
 46         
 47     }
 48 };
 
 

Following Javascript code shows binding and example of usage (again in real application binding will be stored in configurations):

  1 
  2 var vehicleType = new EntityType('Vehicle', {
 
  3     'maker' : 'String',              // name -> typeName
  4     'model' : 'String',
 
  5     'yearCreated' : 'Date',
  6     'speed' : 'Number',
  7     'miles' : 'Number'
 
  8 });
  9 
 10 var vehicle = new Entity(vehicleType, {
 11     'maker' : 'Toyota',
 
 12     'model' : 'Highlander',
 13     'yearCreated' : new Date(2003, 0, 1),
 14     'speed' : 120,
 
 15     'miles' : 3000
 16 });
 17 
 18 vehicle.drive = function() {
 19     }.bind(vehicle);
 
 20 
 21 vehicle.stop = function() {
 22     }.bind(vehicle);
 23 
 24 vehicle.performMaintenance = function() {
 
 25     }.bind(vehicle);

A big difference with dynamic languages is that you can bind properties operations to the objects at runtime so you can invoke them as if they were native. For example, you can invoke vehicleType.maker() to get maker property of the vehicle-type or call vehicle.drive() to invoke operation on vehicle object. Another difference is that a lot of plumbing code disappears with dynamic languages.

Ruby Implementation

Similarly, above example in Ruby may look like:

  1 require 'date'
 
  2 require 'forwardable'
  3 class EntityType
  4   attr_accessor :type_name
 
  5   attr_accessor :property_names_and_types
  6   def initialize(type_name, property_names_and_types)
  7     @type_name = type_name
 
  8     @property_names_and_types = property_names_and_types
  9   end
 10   def property_type(property_name)
 11     @property_names_and_types[property_name]
 
 12   end
 13 end
 14 
 15 
 16 class Entity
 
 17   attr_accessor :entity_type
 18   attr_accessor :properties
 19   def initialize(entity_type, attrs = {})
 
 20     @entity_type = entity_type
 21     bind_properties(entity_type.property_names_and_types)
 22     attrs.each do |name, value|
 23       instance_variable_set("@#{name}", value)
 
 24     end
 25   end
 26   def bind_properties(property_names_and_types)
 27     (class << self; self; end).module_eval do
 
 28       property_names_and_types.each do |name, type|
 29         define_method name.to_sym do
 30           instance_variables_get("@#{name}")
 
 31         end
 32         define_method name.to_sym do
 33           instance_variables_set("@#{name}", value)
 
 34         end
 35       end
 36     end
 37   end
 38 end
 
 39 
 66 
 67 
 68 
 

We can then use Singleton, Lambdas and metaprogramming features of Ruby to add Adaptive Object Model support, e.g.

  1 vehicle_type = EntityType.new('Vehicle', {
 
  2     'maker' => 'String',             # class.name
  3     'model' => 'String',
 
  4     'yearCreated' => 'Time',
  5     'speed' => 'Fixnum',
 
  6     'miles' => 'Float'});
  7 
  8 
  9 vehicle = Entity.new(vehicle_type, {
 
 10     'maker' => 'Toyota',
 11     'model' => 'Highlander',
 12     'yearCreated' => DateTime.parse('1-1-2003'),
 
 13     'speed' => 120,
 14     'miles' => 3000});
 15 class << vehicle
 
 16   def drive
 17     "driving"
 18   end
 19   def stop
 
 20     "stopping"
 21   end
 22   def perform_maintenance
 23     "performing maintenance"
 
 24   end
 25 end
 26 
 27 
 

Ruby code is a lot more succint and as Ruby supports adding or removing methods dynamically, you can invoke properties and operations directly on the objects. For example, you can invoke vehicleType.maker() to get maker property of the vehicle-type or call vehicle.drive() to invoke operation on vehicle object. Also, Ruby provides a lot more options for higher order functions such as monkey patching, lambdas/procs/methods, send, delegates/forwardables, etc. Finally, Ruby provides powerful generative capabilities to build DSL that can bind all properties and operations at runtime similar to how Rails framework work.

Schema-less Databases

Now, the second half of the equation for Adaptive Object Model is persisting, which I have found to be challenge with relational databases. However, as I have been using schemaless databases such as CouchDB, it makes it trivial to store meta information as part of the plain data. For example, if I have to store this vehicle in CouchDB, all I have to do is create a table such as vehicles (I could use Single Table Inheritance to store all types of vehicles in same table):

 curl -XPUT http://localhost:5984/vehicles
 curl -XPUT http://localhost:5984/vehicle_types
 

and then add vehicle-type as

 curl -XPOST http://localhost:5984/vehicle_types/ -d '{"maker":"String", "model":"String", "yearCreated":"Date", "speed":"Number", "miles":"Number"}'
 

which returns

 {"ok":true,"id":"bb70f95e43c3786f72cb46b372a2808f","rev":"1-3976038079"}
 

Now, we can use the id of vehicle-type and add vehicle a follows

 curl -XPOST http://localhost:5984/vehicles/ -d '{"vehicle_type_id":"bb70f95e43c3786f72cb46b372a2808f", "maker":"Toyota", "model":"Highlander", "yearCreated":"2003", "speed":120, "miles":3000}'
 

which returns id of newly created vehicle as follows:

 {"ok":true,"id":"259237d7c041c405f0671d6774bfa57a","rev":"1-367618940"}
 

Summary

It is often said in software development that you can solve any problem with another level of indirection. Adaptive Object Model uses another level of indirection to create powerful applications that meet increasingly changing requirements. When it is used with dynamic languages that support metaprogramming and generative programming, it can be used build systems that can be easily evolved with minimum changes and downtime. Also, Schema-less databases eliminates drawbacks of many implementations of AOM that suffer from poor performance due to excessive joins in the relational databases.

May 26, 2009

Tracing memory leaks with command line Java tools

Filed under: Java — admin @ 3:42 pm

I was recently tracking memory leak in one of our applicatios, however the problem was only occuring in our production environment and the network latency between production subnet and my local subnet was too high to run profiler (I tried). So, I had to use some other tools to look at the snapshot of memory that was in use. I found jmap and jhat that are shipped with JDK (1.6 in our case) pretty handy. When memory usage was high, I dumped the memory using jmap, e.g.

 jmap -F -dump:file=/tmp/dump.out 
 

I then copied the file to my local machine and ran jhat using

 jhat dump.out
 

Though, jhat is fairly slow, but it finally starts the web server on port 7000 and you can then point your browser to

 http://mymachine:7000
 

The jhat can also read data from hprof that you can dump by adding following vm option:

 -agentlib:hprof=heap=sites
 

and then read the file using jhat. In addition to above tool, you can run garbage collection in verbose mode using following vm option:

 -verbose:gc -verbose:class -Xloggc:gc.out -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
 

Finally, you can use jconsole to look at memory graph by adding support of jmx to your application server, .e.g.

 -Dcom.sun.management.jmxremote.port=9003 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote
 

Despite all these options, I find profiler is much better for finding memory leaks, but these tools helped a bit.

May 6, 2009

Integrating Springs event notification with JMS

Filed under: Java — admin @ 3:56 pm
I recently had to deploy a service that needed deployment in cluster fashion, however I wanted to synchronize some cache between the instances of servers. Since that service was making some use of Spring, I decided to leverage Spring’s builtin event notification with some glue to convert those Spring events into JMS based messaging. First, I defined an event that I can use in the application. As Map of Strings to Strings seemed simple, I used it to store message properties, e.g.

  1 import org.springframework.context.ApplicationEvent;
 
  2 import org.apache.commons.lang.builder.ToStringBuilder;
  3 import org.apache.commons.lang.builder.ToStringStyle;
  4 import java.util.Map;
 
  5 import java.util.HashMap;
  6 import java.util.Collection;  
  7                                                                   
  8 public class MessageEvent extends ApplicationEvent {       
 
  9     private String msg;
 10     private Map<String, String> map;
 11     public MessageEvent(final Object source, final String ... properties) {
 
 12         super(source);                                                            
 13         map = new HashMap<String, String>();
 14         for (int i=0; i<properties.length-1; i+=2) { 
 
 15             String name = properties[i];
 16             String value = properties[i+1];
 17             map.put(name, value);
 18         }
 19     }   
 20     public MessageEvent(final Object source, final Map<String, String> map) {
 
 21         super(source);
 22         this.map = map; 
 23     }       
 24     public String getProperty(final String key) {
 
 25         return map.get(key);
 26     }   
 27     public boolean isProperty(final String key) {
 
 28         String value = map.get(key);
 29         if (value == null) {
 30             return false;
 31         }
 
 32         return new Boolean(value).booleanValue();
 33     }   
 34     public Map<String, String> getProperties() {
 
 35         return map;
 36     }   
 37     @Override
 38     public String toString() {
 39         return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
 
 40                 .append("source", getSource())
 41                 .append("map", this.map)
 42                 .toString();
 43     }
 
 44 }
 45 
 46 
 

I then created a class to convert above message into JMS message, e.g.

  1 import javax.jms.Session;
 
  2 import javax.jms.JMSException;
  3 import javax.jms.Message;
  4 import javax.jms.MapMessage;                                      
 
  5                                                            
  6 import org.springframework.jms.support.converter.MessageConversionException;
  7 import org.springframework.jms.support.converter.MessageConverter;
  8                                                                            
 
  9 import java.util.Map;
 10 import java.util.HashMap;
 11 import java.util.Enumeration;
 12                                         
 
 13 public class MapMessageConverter implements MessageConverter {
 14     public Object fromMessage(final Message message) throws JMSException, MessageConversionException {
 
 15         if (!(message instanceof MapMessage)) {
 16             throw new MessageConversionException("Message isn't a MapMessage");
 
 17         }
 18         MapMessage mapMessage = (MapMessage) message;
 19         Map<String, String> map = new HashMap<String, String>();
 
 20         Enumeration<String> en = mapMessage.getMapNames();
 21         while (en.hasMoreElements()) {
 22             String name = en.nextElement();
 23             String value = mapMessage.getString(name);
 
 24             map.put(name, value);
 25         }
 26         return map;
 27     }       
 28     public Message toMessage(final Object object, final Session session) throws JMSException, MessageConversionException {
 
 29         if (!(object instanceof Map)) {
 30             throw new MessageConversionException("Object isn't a Map");
 
 31         }
 32         Map<String, String> map = (Map<String, String>) object;
 33         MapMessage message = session.createMapMessage();
 34         for (Map.Entry<String, String> e : map.entrySet()) {
 
 35             message.setString(e.getKey(), e.getValue());
 36         }
 37         return message;
 38     }           
 39 }              
 
 40 
 

Next I created a class that listened for Spring application event and converted into JMS message and published it:

  1 import javax.jms.JMSException;
 
  2 import javax.jms.Session;
  3 import javax.jms.Message;
  4 import java.util.Map;
 
  5 import java.util.UUID;
  6 import org.apache.log4j.Logger;                            
  7 import org.springframework.jms.core.JmsTemplate;                            
 
  8 import org.springframework.jms.core.MessageCreator;               
  9 import org.springframework.jms.support.converter.MessageConverter;         
 10 import org.springframework.context.ApplicationEvent;
 
 11 import org.springframework.context.ApplicationListener;
 12     
 13 public class PublisherAdapter implements ApplicationListener {
 
 14     private static final Logger logger = Logger.getLogger(PublisherAdapter.class);
 15     private JmsTemplate jmsTemplate;
 16     private MessageConverter converter;
 
 17             
 18     //      
 19     public void setMessageConverter(final MessageConverter converter) {
 20         this.converter = converter;
 
 21     }
 22         
 23     public void setJmsTemplate(final JmsTemplate jmsTemplate) {
 24         this.jmsTemplate = jmsTemplate;
 
 25     }
 26         
 27     public void publish(final Map<String, String> map) {
 
 28         jmsTemplate.send(new MessageCreator() {
 29                 public Message createMessage(final Session session) throws JMSException {
 
 30                     return converter.toMessage(map, session);
 31                 }
 32             });
 33     }   
 34     
 35     public void onApplicationEvent(final ApplicationEvent event) {
 
 36        if (event.getSource() != converter && event instanceof MessageEvent) {
 37            publish(((MessageEvent)event).getProperties());
 38        }        
 39     }           
 
 40 }
 41 
 42 
 

Then, I created a JMS listener that listened for messages on Topic and converted those into Spring application event:

  1 import org.apache.log4j.Logger;
 
  2 import java.util.Map;
  3 import javax.jms.MessageListener;
  4 import javax.jms.Message;
 
  5 import javax.jms.MapMessage;
  6 import javax.jms.JMSException;
  7 import org.springframework.jms.support.converter.MessageConverter;
 
  8 import org.springframework.context.ApplicationContext;
  9 import org.springframework.context.ApplicationContextAware;
 10 import org.springframework.beans.BeansException;
 
 11 
 12 
 13 public class ListenerAdapter implements MessageListener, ApplicationContextAware {
 14     private static final Logger logger = Logger.getLogger(ListenerAdapter.class);
 
 15     private MessageConverter converter;
 16     private ApplicationContext applicationContext;
 17             
 18     public void setMessageConverter(final MessageConverter converter) {
 
 19         this.converter = converter;
 20     }   
 21     public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
 
 22         this.applicationContext = applicationContext;
 23             
 24     }       
 25             
 26     public void onMessage(final Message message) {
 
 27         Map<String, String> map = (Map<String, String>) converter.fromMessage(message);
 28         applicationContext.publishEvent(new MessageEvent(this, map));
 29     }   
 
 30 }       
 31 
 32 
 

Next, here is Spring configuration to bootstrap these listeners:

  1 <?xml version="1.0" encoding="UTF-8"?>
 
  2 <beans>
  3    <bean id="mapMessageConverter" class="com.amazon.jasper.messaging.spring.MapMessageConverter"/>
 
  4   <bean id="springTopic" class="org.apache.activemq.command.ActiveMQTopic">
  5       <constructor-arg index="0" value="springTopic"/>
 
  6    </bean>
  7    <bean id="springJmsTemplate" class="org.springframework.jms.core.JmsTemplate" scope="prototype">
 
  8         <property name="connectionFactory" ref="jmsConnectionFactory"/>
  9         <property name="deliveryPersistent" value="true"/>
 
 10         <property name="messageConverter" ref="mapMessageConverter"/>
 11         <property name="defaultDestination" ref="springTopic"/>
 
 12    </bean>
 13    <bean id="publisherAdapter" class="com.amazon.jasper.messaging.spring.PublisherAdapter" scope="prototype">
 
 14         <property name="jmsTemplate" ref="springJmsTemplate"/>
 15         <property name="messageConverter" ref="mapMessageConverter"/>
 
 16    </bean>
 17    <bean id="springTopicListener" class="com.amazon.jasper.messaging.spring.ListenerAdapter" scope="prototype">
 
 18         <property name="messageConverter" ref="mapMessageConverter"/>
 19    </bean>
 
 20    <bean class="org.springframework.jms.listener.DefaultMessageListenerContainer" init-method="start" destroy-method="stop" scope="prototype">
 
 21         <property name="connectionFactory" ref="jmsConnectionFactory"/>
 22         <property name="destination" ref="springTopic"/>
 
 23         <property name="messageListener" ref="springTopicListener"/>
 24         <property name="transactionManager" ref="jmsTransactionManager"/>
 
 25         <property name="concurrentConsumers" value="10"/>
 26    </bean>
 
 27 </beans>
 28 
 29 
 

Finally, here is how you will actually use this plumbing in your code:

  1 import org.springframework.context.ApplicationContext;
 
  2 import org.springframework.context.ApplicationContextAware;
  3 import org.springframework.context.ApplicationEvent;
  4 import org.springframework.context.ApplicationListener;
 
  5 import com.amazon.jasper.workflow.WorkflowContext;
  6 public class Myclass implements ApplicationListener, ApplicationContextAware {
 
  7     private ApplicationContext ctx;
  8 
  9     //  ...
 10             ctx.publishEvent(new MessageEvent(this, SYNC_ID, syncId, SYNC_XPDL, "true"));
 
 11 
 12     public void setApplicationContext(ApplicationContext applicationContext) {
 13         this.ctx = applicationContext;
 14     }
 15 
 
 16     public void onApplicationEvent(ApplicationEvent event) {
 17             if (event instanceof MessageEvent) {
 18                 MessageEvent msgEvt = (MessageEvent)event;
 
 19                 // do cache coherence
 20             }
 21     }
 22 
 23 
 

All you need is to add your class to your Spring configuration file, it will automatically be registered as listener for spring events. All this is fairly simple, but I hope it helps you for similar uses in your code.

January 15, 2009

Tips from Effective Java, 2nd edition

Filed under: Java — admin @ 1:56 pm

I have been using Java since ’95 and I just reread Effective Java Second Edition by by Joshua Bloch, it is must have reference book for any Java programmer. It consists of over 75 guidelines for writing better code in Java or any other object oriented language for that better. I describe some of my favorite tips from the book:

Factories/Builders

Though, this book does not cover design patterns in general but encourages use of factories instead of constructors in certain cases. I also use this advice in cases when I have a lot of constructors and it is difficult to tell which constructor should be used. The factory methods also allows instantiating and returning subclass types. Though, the downside is that factory methods look like other static methods so it may not be obvious. I also use builders when I need to create a class with a lot of parameters and some of those parameters are optional. I often use following interface to build objects:

 public interface Builder<T> {
     public T build();
 }
 

Use enum for defining Singleton

As Java does not offer language level construct for defining Singletons (as opposed to Scala), defining Singleton can be difficult. The general rule I follow is to make the constructor private and define a static method for getting instance. However, there are still gotchas of preserving Singletons especially upon deserialization. Joshua recommends using enum because it automatically takes care of those edge cases.

Avoid finalize

Since, Java borrowed much of its syntax from C++, it created finalize method for a sort of desctructor. It is often misunderstood by new Java developers because it’s not a destructor. Also, it may not be called by the Java at all. Joshua recommends using try/finally block for any kind of resource allocation/release. I think closures in Java 7 may also help in this regard.

Generics

There are tons of gotchas with using generics in Java. One of my favorite tip to reduce amount of code is to define helper methods is to type inferencing, e.g.

   public static <K,V> Map<K, V> newMap() {
         return new HashMap<K, V>();
   }
 

One of confusing part of generics is when to use extend vs super, the book gives easy acronym PECS (producer extend consumer super) for remembering it. For example, following code shows a method that adds items to a collection:

   void popall(Collection<? super E> d) {
     d.add(pop());
   }
   void pushAll(Iterable<? extends E> src) {
      for (E e : src)
         push(e);
   }
 
 

Functional Programming

Though, Java is not a functional language, but Joshua offers some tips on creating functors or function objects, e.g.

         interface UnaryFunction<T> {
            public T apply(T arg);
         }
 

Now this interface can be implemented for various operations and give a flavor of functional programming. Again, closures in Java 7 would help in this regard.

EnumSet and EnumMap

I have rarely used EnumSet and EnumMap in practice, but the book offers useful tips for using those instead of bit masking and manipulation.

Threading

Though, I like Java Concurrency in Practice for concurrency and threading related topics. But the book offers good tips such as using immutable classes and use of synchronization features of Java for writing thread-safe applications. One of my favorite tip is to use double check idiom, I learned that when Java initially came out and synchronization was somewhat expensive. However, I stopped using it due to some of concerns in Java memory model. Java 1.5 has fixed the Java memory model so we can use it again by declaring the shared field as volatile and doing something like:

   volatile FieldHolder fh;
   ...
   void foo() {
     FieldHolder ref = fh;
     if (ref == null) {
         synchronized (this) {
             ref = fh;
             if (ref == null) {
                fh = ref = compute();
             }
         }
     }
   }
 

Another tip is to use lazy init holder pattern for initializing static field lazily that requires some synchronization, e.g.

 class MyClass {
     static class FieldHolder {
         static Field field = compute();
     }
     static Field get() {
         return FieldHolder.field;
     }
     static synchronized Field compute() {
        ...
     }
 }
 

Other threading tips include use of Executors and classes from new Java concurrency library (java.util.concurency).

Cloning and Serialization

The short answer for cloning is not to use it and instead use constructors to copy objects. The serialization creates a lot of security problems and can be difficult when using inheritance.

Exceptions

Joshua also offers a lot of good advice for exceptions such as using exceptions with proper abstraction level. Though, he still suggests use of checked exceptions for recoverable errors but my suggestion is to use RuntimeException for both application/non-recoverable exceptions and system/recoverable exceptions.

equals, hashCode, toString, Comparable

Writing equals method when inheritance is used can also be difficult and Joshua offers a lot of good advice on writing correct equals method. Another gotcha is to make sure hashCode matches semantics of equals method. Also, it good idea to implement Comparable interface so that it’s easy to use sorted collections.

Conclusion

I briefly wrote some of my favorite tips from the book, again it’s absolute desk reference book.

October 22, 2008

Developing REST based services using JSR 311

Filed under: Java — admin @ 1:28 pm

Recently, I had to write a few services using REST style in Java. Though I have developed such services in past either using Java Servlet APIs, but I wanted to find something better. I had found writing REST style services in Ruby on Rails a breeze and I wanted something as testable and easy to configure. I found a new JSR 311 that does exactly that. Similar to EJB 3.0 and Servlet 3.0 trend, it allows you to write REST services using annotations. I found an open source project that implements JSR 311 and just released 1.0. I found most of the documentation was Maven and Glassfish driven and I wanted to simply try on Tomcat and Ant so here is how I did it:

Download and Install

First, download following jar libraries (you can see that I am using jars from maven repository because Jersey’s wiki was missing jars or pointing to old version):

Developing

My application used models to describe business object, DAOs for database access and I didn’t want to pollute my business objects with REST annotations so I created two new packages for resources and services. Here is a simple example that follows this separation of concerns:

Business Objects (Model)

The model simply consists of a contact class that stores contact information for a person.

 
 package rest;
 
 public class Contact {
     private final String name;
     private final String email;
     private final String address;
 
     public Contact(final String name, final String email, final String address) {
         this.name = name;
         this.email = email;
         this.address = address;
     }
 
 
     public String getName() {
         return name;
     }
 
     public String getEmail() {
         return email;
     }
 
     public String getAddress() {
         return address;
     }
 }
 

Data Access Layer

The data access layer simply uses a hashmap for storing and accessing these contacts, e.g.

 
 package rest;
 
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
 public class ContactDao {
     private static Map repository = new HashMap();
 
     public boolean save(Contact c) {
         boolean created = repository.get(c.getName()) != null;
         repository.put(c.getName(), c);
         return created;
     }
 
     public boolean delete(String name) {
         return repository.remove(name) != null;
     }
 
     public Contact get(String name) {
         return repository.get(name);
     }
 
     public Collection getAll() {
         return repository.values();
     }
 }
 
 

Resources

For resources, I defined ContactResource that adds XML annotations to convert Contact into XML and ContactsResponse for returning complete XML response, e.g.

 
 package rest;
 import javax.ws.rs.core.UriInfo;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 
 import org.codehaus.jettison.json.JSONException;
 import org.codehaus.jettison.json.JSONObject;
                                                                                                                                                       
 
 @XmlAccessorType(XmlAccessType.PROPERTY)
 @XmlRootElement(name = "Contact")
 public class ContactResource {
     private UriInfo uriInfo;
     private Contact contact;
 
     public ContactResource(final UriInfo uriInfo, final String name, final String email, final String address) {
         this.uriInfo = uriInfo;
         this.contact = new Contact(name, email, address);
     }
 
 
     public ContactResource(final Contact contact) {
         this.contact = contact;
     }
 
 
     ContactResource() {
     }
 
 
     public JSONObject toJson() throws JSONException {
         return new JSONObject()
             .put("name", contact.getName())
             .put("email", contact.getEmail())
             .put("address",contact.getAddress());
     }
 
     @XmlElement(name = "Name")
     public String getName() {
         return contact.getName();
     }
 
     @XmlElement(name = "Email")
     public String getEmail() {
         return contact.getEmail();
     }
 
     @XmlElement(name = "Address")
     public String getAddress() {
         return contact.getAddress();
     }
 }
 
 
 

 
 package rest;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 
 
 
 @XmlAccessorType(XmlAccessType.PROPERTY)
 @XmlRootElement(name = "ContactsResponse")
 public class ContactsResponse {
     private String uri;
     private String status;
     private Collection contacts;
 
     public ContactsResponse(String uri, String status, Collection contacts) {
         this.uri = uri;
         this.status = status;
         this.contacts = new ArrayList();
         for (Contact contact : contacts) {
             this.contacts.add(new ContactResource(contact));
         }
     }
 
     public ContactsResponse(String uri, String status, Contact contact) {
         this(uri, status, contact == null ? new ArrayList() : Collections.singleton(contact));
     }
 
     ContactsResponse() {
     }
 
     @XmlElement(name = "Uri")
     public String getUri() {
         return uri;
     }
     public void setUri(String uri) {
         this.uri = uri;
     }
     @XmlElement(name = "Status")
     public String getStatus() {
         return status;
     }
     public void setStatus(String status) {
         this.status = status;
     }
 
     @XmlElement(name = "Contacts")
     public Collection getContactResources() {
         return contacts;
     }
     public void setContactResources(Collection contacts) {
         this.contacts = contacts;
     }
 }
 
 

Service

Here is the meat of JSR 311 that defines annotations for the REST based web service, i.e.,

 
 package rest;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Collection;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.FormParam;
 import javax.ws.rs.GET;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Request;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
 import org.codehaus.jettison.json.JSONArray;
 import org.codehaus.jettison.json.JSONException;
 import org.springframework.context.annotation.Scope;
 
 import com.sun.jersey.api.spring.Autowire;
 
 @Path("/contacts/")
 @Produces( { "application/json", "application/xml" })
 @Consumes( { "application/json", "application/xml" })
 @Scope("singleton")
 @Autowire
 public class ContactService {
     private final ContactDao contactDao;
     @Context UriInfo uriInfo;
     @Context Request request;
 
     public ContactService(ContactDao dao) {
         this.contactDao = dao;
     }
 
     public ContactService() {
         this(new ContactDao()); // this will be injected in real-app
     }
 
     /**
      * create contact
      */
     @PUT
     @Consumes("*/*")
     @Produces("application/xml")
     @Path("{name}")
     public Response createcontact(
             @PathParam("name") String name,
             @FormParam("email") String email,
             @FormParam("address") String address) {
         Contact contact = new Contact(name, email, address);
         final boolean newRecord = contactDao.save(contact);
         if (newRecord) {
             try {
                 URI uri = uriInfo != null ? uriInfo.getAbsolutePath()
                         : new URI("/contacts/");
                 return Response.created(uri).build();
             } catch (URISyntaxException e) {
                 throw new RuntimeException("Failed to create uri", e);
             }
         } else {
             return Response.noContent().build();
         }
     }
 
     /**
      * deletes contact 
      * 
      */
     @DELETE
     @Consumes("*/*")
     @Path("{name}")
     public Response deletecontact(@PathParam("name") String name) {
         boolean deleted = contactDao.delete(name);
         if (deleted) {
             return Response.ok().build();
         } else {
             return Response.status(404).build();
         }
     }
 
     /**
      * @return contact in XML format
      */
     @GET
     @Consumes({"text/xml", "application/xml"})
     @Produces("application/xml")
     @Path("{name}")
     public ContactsResponse getcontactByXml(
             @PathParam("name") String name) {
         Contact contact = contactDao.get(name);
         String uri = uriInfo != null ? uriInfo.getAbsolutePath().toString() : "/contacts/";
         return new ContactsResponse(uri, "success", contact);
     }
 
     /**
      * @return contact in JSON format
      */
     @GET
     @Consumes("application/json")
     @Produces("application/json")
     @Path("{name}")
     public JSONArray getcontactByJson(@PathParam("name") String name) throws JSONException {
         Contact contact = contactDao.get(name);
         JSONArray arr = new JSONArray();
         arr.put(new ContactResource(contact).toJson());
         return arr;
     }
 
     /**
      * @return all contacts in XML format
      */
     @GET
     @Consumes({"text/xml", "application/xml"})
     @Produces("application/xml")
     public ContactsResponse getAllByXml() {
         Collection contacts = contactDao.getAll();
         String uri = uriInfo != null ? uriInfo.getAbsolutePath().toString() : "/contacts/";
         return new ContactsResponse(uri, "success", contacts);
     }
 
     /**
      * @return contacts in JSON format
      */
     @GET
     @Consumes("application/json")
     @Produces("application/json")
     public JSONArray getAllByJson() throws JSONException {
         Collection contacts = contactDao.getAll();
         JSONArray arr = new JSONArray();
         for (Contact contact : contacts) {
             arr.put(new ContactResource(contact).toJson());
         }
         return arr;
     }
 }
 
 

A few things to note:

  • @Path defines the URI used for accessing the service
  • I am using @PUT to store contacts (as the user is creating new URI as opposed to @POST where the application uses same URI). Also, I don’t have any method for update (which uses PUT) as I already am using PUT and create method simply updates the contact if it already exist.
  • @PathParam is retrieved from the URI itself, e.g. /contacts/myname
  • @FormParam is retrieved from POST form submission
  • I can use the same URI and return different type of data based on Content-Type, e.g. when user sets it to application/xml or text/xml I return XML content and when user sets it to application/json I return JSON format.
  • To return list of contacts I skip the name and simply use /contacts/

Servlet Configuration

I added servlet to handle REST requests to web.xml, e.g.

     <servlet>
         <servlet-name>RestServlet</servlet-name>
         <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
         <init-param>
             <param-name>com.sun.jersey.config.feature.Redirect</param-name>
             <param-value>true</param-value>
         </init-param>
         <init-param>
             <param-name>com.sun.jersey.config.feature.ImplicitViewables</param-name>
             <param-value>true</param-value>
         </init-param>
         <init-param>
             <param-name>com.sun.jersey.config.property.packages</param-name>
             <param-value>rest</param-value>
         </init-param>
         <load-on-startup>1</load-on-startup>
     </servlet>
     <servlet-mapping>
         <servlet-name>RestServlet</servlet-name>
         <url-pattern>/svc/*</url-pattern>
     </servlet-mapping>
 

I used Spring to inject DAOs in real application but if you don’t need it then replace com.sun.jersey.spi.spring.container.servlet.SpringServlet with com.sun.jersey.spi.container.servlet.ServletContainer.

Also note that com.sun.jersey.config.property.packages defines package name of Java classes that defines service classes.

Deploying

I am assuming you already know how to package a war file and deploy it to application server such as Tomcat.

Testing it

Unit Testing it

A big advantage of JSR 311 is ease of test, e.g. here is a sample unit test:

 
 package rest;
 import javax.ws.rs.core.Response;
 
 import junit.framework.TestCase;
 import org.codehaus.jettison.json.JSONArray;
 import org.codehaus.jettison.json.JSONObject;
 
 import com.sun.jersey.api.NotFoundException;
 
 public class ContactServiceTest extends TestCase {
     private ContactService service;
 
     @Override
     protected void setUp() throws Exception {
         service = new ContactService();
     }
 
     public void testCreateContactWithGetContactByXml() {
         final String name = "shahbhat";
         final String email = "shahbhat@myhost";
         final String address = "shahbhat address ";
 
         Response response = service.createContact(name, email, address);
         assertEquals(201, response.getStatus());
         assertNull(response.getEntity());
 
         // recreate the same mapping and it should return no content
         response = service.createContact(name, email, address);
         assertEquals(204, response.getStatus());
         assertNull(response.getEntity());
 
         ContactsResponse contactResponse = service.getContactByXml(name);
         assertEquals(1, contactResponse.getContactResources().size());
         ContactResource contactResource = contactResponse.getContactResources().iterator().next();
         assertEquals(name, contactResource.getName());
         assertEquals(email, contactResource.getEmail());
         assertEquals(address, contactResource.getAddress());
     }
 
     public void testCreateContactWithGetContactByJson() throws Exception {
         final String name = "shahbhat";
         final String email = "shahbhat@myhost";
         final String address = "shahbhat address ";
 
         Response response = service.createContact(name, email, address);
         assertEquals(201, response.getStatus());
         assertNull(response.getEntity());
 
         JSONArray jsonArray = service.getContactByJson(name);
         assertEquals(1, jsonArray.length());
         JSONObject json = jsonArray.getJSONObject(0);
         assertEquals(name, json.getString("name"));
         assertEquals(email, json.getString("email"));
         assertEquals(address, json.getString("address"));
     }
 
     public void testDeleteContact() {
         final String name = "shahbhat";
         final String email = "shahbhat@myhost";
         final String address = "shahbhat address ";
 
         Response response = service.createContact(name, email, address);
         assertEquals(201, response.getStatus());
         assertNull(response.getEntity());
 
         ContactsResponse contactResponse = service.getContactByXml(name);
         assertEquals(1, contactResponse.getContactResources().size());
         ContactResource contactResource = contactResponse.getContactResources().iterator().next();
         assertEquals(name, contactResource.getName());
         assertEquals(email, contactResource.getEmail());
         assertEquals(address, contactResource.getAddress());
 
         response = service.deleteContact(name);
         assertEquals(200, response.getStatus());
 
         contactResponse = service.getContactByXml(name);
         assertEquals(0, contactResponse.getContactResources().size());
 
         //
         response = service.deleteContact(name);
         assertEquals(404, response.getStatus());
 
     }
 
     public void testGetAllContactByXml() {
         service.createContact("shahbhat", "shahbhat email", "shahbhat address");
         service.createContact("bill", "bill email", "bill address");
 
         ContactsResponse contactResponse = service.getAllByXml();
         assertEquals(2, contactResponse.getContactResources().size());
         for (ContactResource contactResource : contactResponse.getContactResources()) {
             if ("shahbhat".equals(contactResource.getName())) {
                 assertEquals("shahbhat email", contactResource.getEmail());
                 assertEquals("shahbhat address", contactResource.getAddress());
             } else if ("bill".equals(contactResource.getName())) {
                 assertEquals("bill email", contactResource.getEmail());
                 assertEquals("bill address", contactResource.getAddress());
             } else {
                 fail("unknown contact " + contactResource);
             }
         }
 
         service.deleteContact("shahbhat");
         service.deleteContact("bill");
 
         contactResponse = service.getAllByXml();
         assertEquals(0, contactResponse.getContactResources().size());
     }
 
 
     public void testGetAllContactByJson() throws Exception {
         service.createContact("shahbhat", "shahbhat email", "shahbhat address");
         service.createContact("bill", "bill email", "bill address");
 
         JSONArray jsonArray = service.getAllByJson();
         assertEquals(2, jsonArray.length());
 
         for (int i=0; i<2; i++) {
             JSONObject json = jsonArray.getJSONObject(i);
             if ("shahbhat".equals(json.getString("name"))) {
                 assertEquals("shahbhat email", json.getString("email"));
                 assertEquals("shahbhat address", json.getString("address"));
             } else if ("bill".equals(json.getString("name"))) {
                 assertEquals("bill email", json.getString("email"));
                 assertEquals("bill address", json.getString("address"));
             } else {
                 fail("unknown contact " + json);
             }
         }
         service.deleteContact("shahbhat");
         service.deleteContact("bill");
 
         jsonArray = service.getAllByJson();
         assertEquals(0, jsonArray.length());
     }
 }
 

Functional Testing it

Once deploy, you can use curl to functionally test it (though there are other automated tools available as well), e.g

Creating Contact

 curl -X PUT -d "email=myemail&address=myaddress"  http://shahbhat.desktop:8080/svc/contacts/bhatti
 

It will create a contact and to retrieve it, use

 curl http://shahbhat.desktop:8080/svc/contacts/bhatti
 

It should return

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?><ContactsResponse><Contacts><Address>myaddress</Address><Email>myemail</Email><Name>bhatti</Name></Contacts><Status>success</Status><Uri>http://shahbhat.desktop:8080/svc/contacts/bhatti</Uri></ContactsResponse>
 

To get JSON format use

 curl --header "Content-Type: application/json" http://shahbhat.desktop:8080/svc/contacts/bhatti
 

and it should return

 [{"name":"bhatti","email":"myemail","address":"myaddress"}]
 

You can create another contact e.g.

 curl -d "email=billemail&address=billaddress"  http://shahbhat.desktop:8080/svc/contacts/bill
 

Now to get all contacts use

 curl http://shahbhat.desktop:8080/svc/contacts/
 

and it will return

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?><ContactsResponse><Contacts><Address>myaddress</Address><Email>myemail</Email><Name>bhatti</Name></Contacts><Contacts><Address>billaddress</Address><Email>billemail</Email><Name>bill</Name></Contacts><Status>success</Status><Uri>http://shahbhat.desktop:8080/svc/contacts/</Uri></ContactsResponse>
 

And to get JSON format use

 curl --header "Content-Type: application/json" http://shahbhat.desktop:8080/svc/contacts/
 

which will return

 [{"name":"bhatti","email":"myemail","address":"myaddress"},{"name":"bill","email":"billemail","address":"billaddress"}]
 

Conclusion

I found this approach is easily testable with standard unit test and easy to develop. Though, there are few glitches, e.g. @QueryParam only works with GET and if you are POSTing to something like /uri?x=yy then you won't see query parameters. But, overall I am happy with the outcome and will continue to use this framework.

October 3, 2008

Traits of good programmers

Filed under: Java — admin @ 11:00 pm

I love programming and have been doing it for more than 20 years. In that lifetime, I have also worked with a lot of good programmers and have discussed many times “what makes a good programmer” informally or during job interviews. Here are some of important traits that I have found in good programmers:

  • Simplicity and Clarity of thinking – I have found good programmers can think clearly and try to understand essense of underlying problem. Good programmers then develop simple solutions with minimal accidental complexity. I have found systems developed by good programs are easier to understand due to clarity of program design.
  • Passionate about programming – Programmming is a profession and I have seen a lot of programmers who considered programming a 9-5 job, who didn’t care about it outside the job. Many of those programmers did the same job for many years without learning anything new. I have found that best programmers care a great deal about programming and have a great passion about it. One way to distinguish programmers with passion is to ask them about side projects or open source projects that they started or worked on.
  • Continuous Learning – I have found good programmers spend a lot of time learning new skills or finding better ways to do things.
  • Generalist – Due to passion and ability to learn new things, good programmers have breadth of knowledge with varying skills and technolgies. They have modeling, UI, application, systems, database and other skills needed for softrware development.
  • Detail oriented – Programming requires a detail oriented person who can think about a problem and all issues that can effect the solution. I have found good programmers can think of all branch logic and all edge cases when solving a problem.
  • Visualize working of programs – Good programs have ability to visualize inner working of programs and can quickly pinpoint problem areas when things break.
  • Excellence vs Perfectionist – Real world projects are constrained by time and resources and good programmers opt for excellence by doing their best within those constraints rather than being perfectionist.
  • Inquisitive and inquiring mind – One of the hardest part of software development is determining key requirements and differentiating between wants and needs. Often stakeholders don’t know what they want. Good programmers work with users to detrmine what’s really needed for the system. Good programmers can probe users about real underlying problem that they are trying solve. In such systems, agile methodology also help where users can see something working and develop iteratively.
  • Communication – Good programmers communicate in multiple ways, they use design or code to communicate with other programmers. Senior programmers often need to sell or persuade other programmers about architecture or new practices.
  • Integrity and Courage – Though, this is a principle of XP but it’s also one of key trait of good programmers. I have found in most companies, management pulls deadlines and requirements out of the hat and good programmers have courage to speak up to the management when they see death march timelines.
  • Humility – This is also another attribute of XP but I have found in good programmers because programming is hard profession and everyone makes mistake. Though, I have also seen a lot of prima donnas who were smart (some of them) but were difficult to work with. On the other hand, I have found good programmers are humble and easy to work with.
  • Responsibility – Good programmers are profesional and they take full responsibility in developing fully tested and working solutions for the users.
  • Golden hammer – Due to being generalist, good programmers is less likely to use same solution or golden hammer to varying problems. Also, due to breadth of skills he/she is less likely to be swayed by technology or language de jure, instead good programmers try to evaluate real benefit of new technologies with some side projects.
  • Toolbox of solutions – Good programmers learn from varying solutions and often recognize patterns and similar problems that they have previously worked on. They use this toolbox of solutions and patterns when approaching new problem.
  • Productive – Often you hear about good programmers being 10 or 20 times more productive. Though, I admit it’s hard to measure productivity but I have found that good programmers are productive mostly due to above traits. For example, they develop simple code which is faster to develop and has less defect. They focus on essential probems that solve real problems. They are faster mostly because they practice alot with side projects and recognize patterns and previously solved solutions. When debugging and trouble shooting they can visualize software systems and can see problem areas. They don’t like to hackup things that will break in two weeks, instead they have courage to tell stakeholders risks of meeting unrealistic deadlines or releasing untested applications.

August 27, 2008

Implementing HTTP Proxy Service with XSL Transformation

Filed under: Java — admin @ 10:19 pm

Recently I had to implement a portal that embedded contents from other web applications. I tried to build a framework for easily adding remote applications without any changes to the remote applications. The framework had following design:

  • For different functionality and pages we stored feature-link and url in configuration, that configuration basically allowed us to find where to
    get the HTML/XHTML render page or form.

  • However, instead of displaying raw HTML, I first converted it into valid XHTML using Tidy and then transformed such that all form submissions
    go through our proxy server. As part of the transformation, I added original form URLs, methods and cookies as hidden fields. This allowed
    me to keep the proxy server without any client state, which was nice for clustering.

  • When the form is submitted, the proxy server intercepts it and then makes the request to the remote application and sends back response. The proxy
    server reads the original target URLs, method types, cookies from the form so that remote application can manage state if it’s using Sessions.

Following are a few simplified classes that I developed for the proxy framework:

Content Transformation

First, I developed HTML to XHTML conversion and transformation to modify forms. Following is the XSLT that I used:

  1 <xsl:stylesheet
 
  2   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  3   version="1.0"
  4   xmlns:xhtml="http://www.w3.org/1999/xhtml"
 
  5   xmlns="http://www.w3.org/1999/xhtml"
  6   exclude-result-prefixes="xhtml">
  7 
 
  8   <xsl:param name="callbackHandler"/>
  9   <xsl:param name="callbackUser"/>
 
 10   <xsl:param name="callbackState"/>
 11 
 12   <xsl:template match="@* | node()">
 
 13     <xsl:copy>
 14       <xsl:apply-templates select="@* | node()"/>
 15     </xsl:copy>
 
 16   </xsl:template>
 17 
 18   <xsl:template match="form">
 19     <xsl:copy>
 
 20       <xsl:apply-templates select="@*"/>
 21       <xsl:attribute name="action" xmlns:java="http://xml.apache.org/xslt/java">
 
 22             <xsl:param name="callbackOriginalActionUrl" select="@action"/>
 23             <xsl:text disable-output-escaping="yes">/web/myproxy?myarg=xxx&amp;otherarg=2</xsl:text>
 
 24             <xsl:value-of select="java:com.plexobject.transform.XslContentTransformer.setAction($callbackHandler, string(@action))" />
 25       </xsl:attribute>
 26       <xsl:attribute name="method" xmlns:java="http://xml.apache.org/xslt/java">
 
 27             <xsl:param name="callbackOriginalMethodType" select="@method"/>
 28             <xsl:text disable-output-escaping="yes">POST</xsl:text>
 
 29             <xsl:value-of select="java:com.plexobject.transform.XslContentTransformer.setMethod($callbackHandler, string(@method))" />
 30       </xsl:attribute>
 31       <xsl:attribute name="id">_Form</xsl:attribute>
 
 32       <xsl:attribute name="name">_Form</xsl:attribute>
 33         <input type="hidden" name="_user" value="{$callbackUser}"/>
 
 34         <input type="hidden" name="_originalActionUrl" value="{@action}"/>
 
 35         <input type="hidden" name="_orginalMethodType" value="{@method}"/>
 
 36         <input type="hidden" name="_userState" value="{$callbackState}"/>
 
 37       <xsl:apply-templates select="node()"/>
 38     </xsl:copy>
 39   </xsl:template>
 
 40 
 41 
 42   <xsl:template match="title"/>
 43 
 44 
 
 45 </xsl:stylesheet>
 46 
 47 
 48 
 

A few things note

  • xsl:param allows passing parameters from the runtime (Java)
  • xsl:template is matching for “form” tag and replaces action/method attributes and adds id/name attributes. It then adds a few input hidden fields
  • Finally, I am removing title tag

ContentTransformer interface

  1 package com.plexobject.transform;
 
  2 
  3 import java.util.Map;
  4 
  5 public interface ContentTransformer {
 
  6     /**
  7      * This method transforms given contents
 
  8      * 
  9      * @param contents
 10      *            - input contents
 
 11      * @param properties
 12      *            - input/output properties for transformation
 
 13      * @return transformed contents
 14      * @throws TransformationException
 
 15      *             - when error occurs while transforming content.
 
 16      */
 17     public String transform(String contents, Map<String, String> properties)
 18             throws TransformationException;
 
 19 }
 20 
 21 
 

ContentTransformer implementation

A few things to note in following implementation:

  • I use JTidy to convert HTML to XHTML
  • I pass some of the parameters to the XSL stylesheet and I also read a few properties back. Though, reading properties back is a bit kludgy but it works.
   1 package com.plexobject.transform;
 
   2 
   3 import java.io.ByteArrayInputStream;
   4 import java.io.ByteArrayOutputStream;
   5 import java.io.InputStream;
 
   6 import java.util.HashMap;
   7 import java.util.Map;
   8 
   9 import javax.xml.transform.Result;
 
  10 import javax.xml.transform.Source;
  11 import javax.xml.transform.Transformer;
  12 import javax.xml.transform.TransformerException;
 
  13 import javax.xml.transform.TransformerFactory;
  14 import javax.xml.transform.stream.StreamResult;
  15 import javax.xml.transform.stream.StreamSource;
 
  16 
  17 import org.w3c.tidy.Tidy;
  18 
  19 public class XslContentTransformer implements ContentTransformer {
 
  20     public static final String ACTION = "form_action";
  21     public static final String METHOD = "form_method";
 
  22 
  23     private static final Map<String, Map<String, String>> xslProperties = new HashMap<String, Map<String, String>>();
 
  24     private volatile Transformer transformer;
  25     private final String xslUri;
  26     private final boolean useTidy;
 
  27 
  28     public XslContentTransformer(final String xslUri, final boolean useTidy) {
 
  29         this.xslUri = xslUri;
  30         this.useTidy = useTidy;
  31     }
  32 
 
  33     public static final String setAction(final String callbackHandler,
  34             final String action) {
 
  35         getPropertiesForCallback(callbackHandler).put(ACTION, action);
  36         return "";
  37     }
  38 
 
  39     public static final String getAction(final String callbackHandler) {
  40         return getPropertiesForCallback(callbackHandler).get(ACTION);
 
  41     }
  42 
  43     public static final String setMethod(final String callbackHandler,
 
  44             final String method) {
  45         getPropertiesForCallback(callbackHandler).put(METHOD, method);
  46         return "";
  47     }
 
  48 
  49     public static final String getMethod(final String callbackHandler) {
  50         return getPropertiesForCallback(callbackHandler).get(METHOD);
 
  51     }
  52 
  53     /**
  54      * This method transforms given contents
 
  55      * 
  56      * @param contents
  57      *            - input contents
 
  58      * @param properties
  59      *            - input/output properties for transformation
 
  60      * @return transformed contents
  61      * @throws TransformationException
 
  62      *             - when error occurs while transforming content.
 
  63      */
  64     public String transform(String contents, Map<String, String> properties)
  65             throws TransformationException {
 
  66         initTransformer();
  67         final long started = System.currentTimeMillis();
  68 
  69         contents = contents.replaceAll("<!--.*?-->", "");
 
  70         InputStream in = new ByteArrayInputStream(contents.getBytes());
  71         if (useTidy) {
  72             in = tidy(in, (int) contents.length());
 
  73         }
  74 
  75         //
  76         final Source xmlSource = new StreamSource(in);
 
  77         final ByteArrayOutputStream out = new ByteArrayOutputStream(
  78                 (int) contents.length());
  79 
 
  80         final Result result = new StreamResult(out);
  81         String callbackHandler = properties.get("callbackHandler");
  82         if (callbackHandler == null) {
 
  83             callbackHandler = Thread.currentThread().getName();
  84         }
  85         final Map<String, String> props = new HashMap<String, String>();
 
  86         xslProperties.put(callbackHandler, props);
  87         transformer.setParameter("callbackHandler", callbackHandler);
  88         for (Map.Entry<String, String> e : properties.entrySet()) {
 
  89             transformer.setParameter(e.getKey(), e.getValue());
  90         }
  91         try {
  92             transformer.transform(xmlSource, result);
 
  93         } catch (TransformerException e) {
  94             throw new TransformationException("Failed to transform " + contents, e);
 
  95         }
  96         properties.put(ACTION, getAction(callbackHandler));
  97         properties.put(METHOD, getMethod(callbackHandler));
  98         xslProperties.remove(callbackHandler);
  99         return new String(out.toByteArray());
 
 100     }
 101 
 102     private static final Map<String, String> getPropertiesForCallback(
 
 103             String callbackHandler) {
 104         final Map<String, String> props = xslProperties.get(callbackHandler);
 105         if (props == null) {
 
 106             throw new NullPointerException(
 107                     "Failed to find properties for callback " + callbackHandler);
 108         }
 109         return props;
 
 110     }
 111 
 112     // no synchronization needed, multiple initialization is acceptable
 113     private final void initTransformer() {
 
 114         if (transformer == null) {
 115             try {
 116                 TransformerFactory transFact = TransformerFactory.newInstance();
 117                 InputStream in = getClass().getResourceAsStream(xslUri);
 
 118                 if (in == null) {
 119                     throw new TransformationException("failed to find xslt "
 120                             + xslUri);
 
 121                 }
 122                 Source xsltSource = new StreamSource(in);
 123                 transformer = transFact.newTransformer(xsltSource);
 124             } catch (TransformationException e) {
 
 125                 throw e;
 126             } catch (RuntimeException e) {
 127                 throw e;
 128             } catch (Exception e) {
 
 129                 throw new TransformationException(
 130                         "Failed to initialize XSL transformer", e);
 131             }
 132         }
 
 133     }
 134 
 135     private final InputStream tidy(InputStream in, int length) {
 136         ByteArrayOutputStream out = new ByteArrayOutputStream(length);
 
 137         Tidy converter = new Tidy();
 138         converter.setTidyMark(false);
 139         converter.setXmlOut(true);
 140         converter.setXmlPi(true);
 
 141         converter.setXmlPIs(true);
 142         converter.setNumEntities(true);
 143         converter.setDocType("omit");
 144         converter.setUpperCaseTags(false);
 
 145         converter.setUpperCaseAttrs(false);
 146         converter.setFixComments(true);
 147         converter.parse(in, out);
 148         return new ByteArrayInputStream(out.toByteArray());
 
 149     }
 150 }
 151 
 152 
 

Proxy

Following interfaces and classes show how GET/POST requests are proxied:

Proxy Interface

  1 package com.plexobject.web.proxy;
 
  2 
  3 import java.io.IOException;
  4 import java.util.Map;
  5 
 
  6 public interface HttpProxy {
  7     /**
  8      * This method issues a GET or POST request based on method and URI URI specified in the ProxyState
 
  9      * and adds given parameters to the request.
 
 10      * 
 11      * @param state
 12      *            - proxy state
 
 13      * @param params
 14      *            - name/value pairs of parameters that are sent to the get
 
 15      *            request
 16      */
 17     public ProxyResponse request(ProxyState state, Map<String, String[]> params)
 
 18             throws IOException;
 19 }
 20 
 21 
 

Proxy Implementation

Following class implements HttpProxy interface using HTTPClient library:

   1 package com.plexobject.web.proxy;
 
   2 
   3 import java.io.IOException;
   4 import java.util.ArrayList;
   5 import java.util.List;
 
   6 import java.util.Map;
   7 
   8 import org.apache.commons.httpclient.Cookie;
   9 import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
 
  10 import org.apache.commons.httpclient.HttpClient;
  11 import org.apache.commons.httpclient.HttpMethodBase;
  12 import org.apache.commons.httpclient.HttpState;
 
  13 import org.apache.commons.httpclient.NameValuePair;
  14 import org.apache.commons.httpclient.cookie.CookiePolicy;
  15 import org.apache.commons.httpclient.methods.GetMethod;
 
  16 import org.apache.commons.httpclient.methods.PostMethod;
  17 import org.apache.commons.httpclient.params.HttpMethodParams;
  18 
  19 import com.plexobject.io.IoUtil;
 
  20 
  21 public class HttpProxyImpl implements HttpProxy {
  22     private static final int CONNECTION_TIMEOUT_MILLIS = 30000;
 
  23 
  24     /**
  25      * This method issues a GET or POST request based on method and URI URI specified in the ProxyState
 
  26      * and adds given parameters to the request.
 
  27      * 
  28      * @param state
  29      *            - proxy state
 
  30      * @param params
  31      *            - name/value pairs of parameters that are sent to the get
 
  32      *            request
  33      */
  34     public ProxyResponse request(ProxyState state, Map<String, String[]> params)
 
  35             throws IOException {
  36         if (state.getMethod() == MethodType.GET) {
  37             return get(state, params);
 
  38         } else {
  39             return post(state, params);
  40         }
 
  41     }
  42 
  43 
  44     /**
  45      * This method issues a GET request on the URI specified in the ProxyState
 
  46      * and adds given parameters to the request.
 
  47      * 
  48      * @param state
  49      *            - proxy state
 
  50      * @param params
  51      *            - name/value pairs of parameters that are sent to the get
 
  52      *            request
  53      */
  54     private ProxyResponse get(ProxyState state, Map<String, String[]> params)
 
  55             throws IOException {
  56         GetMethod method = new GetMethod(state.getUri());
  57         method.setQueryString(toNameValues(params));
 
  58         return doRequest(state, params, method);
  59     }
  60 
  61     /**
 
  62      * This method issues a POST request on the URI specified in the ProxyState
 
  63      * and adds given parameters to the request.
 
  64      * 
  65      * @param state
  66      *            - proxy state
 
  67      * @param params
  68      *            - name/value pairs of parameters that are sent to the POST
 
  69      *            request
  70      */
  71     private ProxyResponse post(ProxyState state, Map<String, String[]> params)
 
  72             throws IOException {
  73         PostMethod method = new PostMethod(state.getUri());
  74         method.setRequestBody(toNameValues(params));
 
  75         return doRequest(state, params, method);
  76     }
  77 
  78     private ProxyResponse doRequest(ProxyState proxyState,
 
  79             Map<String, String[]> params, HttpMethodBase method)
  80             throws IOException {
  81         long started = System.currentTimeMillis();
 
  82         HttpClient client = new HttpClient();
  83         client.getHttpConnectionManager().getParams().setConnectionTimeout(
  84                 CONNECTION_TIMEOUT_MILLIS);
  85         client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
 
  86         method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
  87                 new DefaultHttpMethodRetryHandler(3, false));
  88 
  89         HttpState initialState = new HttpState();
 
  90         for (Cookie cookie : proxyState.getCookies()) {
  91             initialState.addCookie(cookie);
  92         }
  93         client.setState(initialState);
 
  94 
  95         try {
  96             int statusCode = client.executeMethod(method);
  97             String contents = IoUtil.read(method.getResponseBodyAsStream());
 
  98             //
  99             Cookie[] cookies = client.getState().getCookies();
 100             for (Cookie cookie : cookies) {
 101                 proxyState.addCookie(cookie);
 
 102             }
 103 
 104             return new ProxyResponse(statusCode, contents, proxyState);
 105         } catch (RuntimeException e) {
 
 106             throw e;
 107         } catch (IOException e) {
 108             throw e;
 109         } catch (Exception e) {
 
 110             throw new IOException("failed to process request", e);
 111         } finally {
 112             method.releaseConnection();
 
 113         }
 114     }
 115 
 116     private NameValuePair[] toNameValues(Map<String, String[]> params) {
 117         if (params == null || params.size() == 0) {
 
 118             return new NameValuePair[0];
 119         }
 120         List<NameValuePair> nvPairs = new ArrayList<NameValuePair>();
 
 121         for (Map.Entry<String, String[]> e : params.entrySet()) {
 122             String[] values = e.getValue();
 123             for (int j = 0; j < values.length; j++) {
 
 124                 nvPairs.add(new NameValuePair(e.getKey(), values[j]));
 125             }
 126         }
 127         return (NameValuePair[]) nvPairs.toArray(new NameValuePair[nvPairs
 
 128                 .size()]);
 129     }
 130 }
 131 
 132 
 

ProxyState

Following class maintains URL, cookies, headers, and other information related to web request:

   1 package com.plexobject.web.proxy;
 
   2 
   3 import java.io.Serializable;
   4 import java.io.UnsupportedEncodingException;
   5 import java.net.URLDecoder;
 
   6 import java.net.URLEncoder;
   7 import java.util.Collection;
   8 import java.util.Date;
 
   9 import java.util.HashMap;
  10 import java.util.Map;
  11 
  12 import org.apache.commons.httpclient.Cookie;
 
  13 
  14 /**
  15  * Class: ProxyState
 
  16  * 
  17  * Description: This class stores state needed to make a proxy request including
 
  18  * method type and cookies.
  19  * 
 
  20  */
  21 public class ProxyState implements Serializable {
  22     private static final long serialVersionUID = 1L;
 
  23     private static final String DATA_DELIMITER = "\n";
  24     private static final String COOKIE_DELIMITER = ";";
 
  25     private static final String NULL = "null";
  26 
  27     private String uri;
 
  28     private MethodType method;
  29     private Map<String, Cookie> cookies;
  30 
 
  31     /**
  32      * Constructors for ProxyState
  33      */
 
  34     public ProxyState(String uri, String method) {
  35         this(uri, MethodType.valueOf(method));
  36     }
  37 
 
  38     public ProxyState(String uri, MethodType method) {
  39         this.uri = uri;
  40         this.method = method;
  41         this.cookies = new HashMap<String, Cookie>();
 
  42     }
  43 
  44     /**
  45      * @return uri
 
  46      */
  47     public String getUri() {
  48         return this.uri;
 
  49     }
  50 
  51     /**
  52      * @return method
 
  53      */
  54     public MethodType getMethod() {
  55         return this.method;
 
  56     }
  57 
  58     /**
  59      * @return cookies
 
  60      */
  61     public Collection<Cookie> getCookies() {
  62         return this.cookies.values();
 
  63     }
  64 
  65 
  66     /**
  67      * @param cookies
 
  68      */
  69     public void addCookies(Collection<Cookie> cookies) {
  70         for (Cookie cookie : cookies) {
 
  71             addCookie(cookie);
  72         }
  73     }
  74 
  75     /**
 
  76      * @param cookie
  77      *            - to add
 
  78      */
  79     public void addCookie(Cookie cookie) {
  80         this.cookies.put(cookie.getName(), cookie);
 
  81     }
  82 
  83     public String getCookieString() {
  84         StringBuilder sb = new StringBuilder(512);
 
  85         for (Cookie cookie : cookies.values()) {
  86             if (cookie.getDomain() != null) {
  87                 sb.append(cookie.getDomain()).append(COOKIE_DELIMITER);
 
  88             } else {
  89                 sb.append(NULL).append(COOKIE_DELIMITER);
  90             }
  91             sb.append(cookie.getName()).append(COOKIE_DELIMITER).append(
 
  92                     cookie.getValue()).append(COOKIE_DELIMITER);
  93 
  94             if (cookie.getPath() != null) {
  95                 sb.append(cookie.getPath()).append(COOKIE_DELIMITER);
 
  96             } else {
  97                 sb.append(NULL).append(COOKIE_DELIMITER);
  98             }
  99             if (cookie.getExpiryDate() != null) {
 
 100                 sb.append(String.valueOf(cookie.getExpiryDate().getTime()))
 101                         .append(COOKIE_DELIMITER);
 102             } else {
 103                 sb.append(NULL).append(COOKIE_DELIMITER);
 104             }
 
 105             sb.append(String.valueOf(cookie.getSecure()))
 106                     .append(DATA_DELIMITER);
 107         }
 108         return sb.toString();
 109     }
 
 110 
 111 
 112     @Override
 113     public String toString() {
 114         StringBuilder sb = new StringBuilder(512);
 
 115         sb.append(uri.toString()).append(DATA_DELIMITER);
 116         sb.append(method.toString()).append(DATA_DELIMITER);
 117         sb.append(getCookieString());
 118         return sb.toString();
 119     }
 
 120 
 121     /**
 122      * This method converts proxy state into string based serialized state
 
 123      * 
 124      * @return string based serialized state
 
 125      */
 126     public String toExternalFormat() {
 127         try {
 128             return URLEncoder.encode(toString(), "UTF8");
 
 129         } catch (UnsupportedEncodingException e) {
 130             throw new IllegalStateException("failed to encode", e);
 131         }
 
 132     }
 133 
 134     /**
 135      * This method converts a string based serialized state into the proxy state
 
 136      * 
 137      * @param ser
 138      *            - string based serialized state
 
 139      * @return ProxyState
 140      * @throws IllegalArgumentException
 141      *             - if serialized state is null or corrupted.
 
 142      */
 143     public static ProxyState valueOf(String ser) {
 144         if (ser == null)
 
 145             throw new IllegalArgumentException("Null serialized object");
 146         String decoded;
 147         try {
 
 148             decoded = URLDecoder.decode(ser, "UTF8");
 149         } catch (UnsupportedEncodingException e) {
 150             throw new IllegalArgumentException("Unsupported encoding " + ser, e);
 
 151         }
 152         String[] lines = decoded.split(DATA_DELIMITER);
 153         if (lines.length < 2)
 154             throw new IllegalArgumentException(
 
 155                     "Insufficient number of tokens in serialized object ["
 156                             + decoded + "]");
 157         ProxyState state = new ProxyState(lines[0], lines[1]);
 158         for (int i = 2; i < lines.length; i++) {
 
 159             String[] cookieFields = lines[i].split(COOKIE_DELIMITER);
 160             if (cookieFields.length < 6)
 161                 throw new IllegalArgumentException(
 
 162                         "Insufficient number of tokens 6 in serialized cookies ["
 163                                 + lines[i] + "]/[" + decoded + "]");
 164             String domain = cookieFields[0];
 165             if (NULL.equals(domain)) {
 
 166                 domain = null;
 167             }
 168             String name = cookieFields[1];
 169             String value = cookieFields[2];
 170             String path = cookieFields[3];
 
 171             if (NULL.equals(path)) {
 172                 path = null;
 173             }
 174             Date expires = null;
 
 175             if (!NULL.equals(cookieFields[4])) {
 176                 expires = new Date(Long.parseLong(cookieFields[4]));
 177             }
 178             boolean secure = new Boolean(cookieFields[5]).booleanValue();
 
 179             Cookie cookie = new Cookie(domain, name, value, path, expires,
 180                     secure);
 181             state.addCookie(cookie);
 182         }
 183         return state;
 
 184     }
 185 
 186     @Override
 187     public boolean equals(Object o) {
 188         if (this == o)
 
 189             return true;
 190         if (!(o instanceof ProxyState))
 191             return false;
 
 192         final ProxyState other = (ProxyState) o;
 193         if (uri != null ? !uri.equals(other.uri) : other.uri != null)
 194             return false;
 
 195         if (method != null ? !method.equals(other.method)
 196                 : other.method != null)
 197             return false;
 
 198         return true;
 199     }
 200 
 201     @Override
 202     public int hashCode() {
 
 203         int result;
 204         result = (uri != null ? uri.hashCode() : 0);
 205         result = 29 * result + (method != null ? method.hashCode() : 0);
 
 206         return result;
 207     }
 208 }
 209 
 210 
 

ProxyResponse

Following class stores response from the HttpProxy interface:

  1 package com.plexobject.web.proxy;
 
  2 
  3 import java.io.Serializable;
  4 
  5 
  6 /**
 
  7  * Class: ProxyResponse
  8  * 
  9  * Description: This class stores proxy state and response.
 
 10  */
 11 public class ProxyResponse implements Serializable {
 12     private static final long serialVersionUID = 1L;
 
 13     private int responseCode;
 14     private String contents;
 15     private ProxyState state;
 
 16 
 17     /**
 18      * Constructor for ProxyResponse
 19      */
 
 20     public ProxyResponse(int responseCode, String contents, ProxyState state) {
 21         this.responseCode = responseCode;
 22         this.contents = contents;
 23         this.state = state;
 
 24     }
 25 
 26     /**
 27      * @return http response code
 
 28      */
 29     public int getResponseCode() {
 30         return this.responseCode;
 
 31     }
 32 
 33     /**
 34      * @return XHTML contents
 
 35      */
 36     public String getContents() {
 37         return this.contents;
 38     }
 
 39 
 40     /**
 41      * @return state associated with the proxy web request
 
 42      */
 43     public ProxyState getState() {
 44         return this.state;
 45     }
 
 46 
 47     @Override
 48     public String toString() {
 49         return this.responseCode + "\n" + this.state + "\n" + this.contents;
 
 50     }
 51 }
 52 
 53 
 

MethodType

Following class defines enum for http method types:

  1 package com.plexobject.web.proxy;
 
  2 
  3 /**
  4  * Class: MethodType
 
  5  * 
  6  * Description: Defines supported method types for proxy request.
 
  7  * 
  8  */
  9 public enum MethodType {
 
 10     GET, POST;
 11 }
 12 
 13 
 

Service Example

Following classes show how above HTTPProxy and ContentTransfomer interfaces can be used with Servlet/Portlet APIs:

ProxyService Interface

 1 package com.plexobject.web.service;
 
 2 import javax.servlet.http.*;
 3 import java.io.*;
 4 
 5 public interface ProxyService {
 
 6     public void render(HttpServletRequest request,  HttpServletResponse response) throws IOException ;
 7     public void submit(HttpServletRequest request,  HttpServletResponse response) throws IOException ;
 
 8 }
 9 
 0 
 

ProxyService Implementation

  1 package com.plexobject.web.service;
 
  2 import com.plexobject.web.proxy.*;
  3 import com.plexobject.transform.ContentTransformer;
  4 import javax.servlet.http.*;
 
  5 import java.io.*;
  6 import java.util.*;
  7 
  8 
 
  9 public class ProxyServiceImpl implements ProxyService {
 10     private HttpProxy httpProxy;
 11     private ContentTransformer contentTransformer;
 
 12     public ProxyServiceImpl(HttpProxy httpProxy, ContentTransformer contentTransformer) {
 13         this.httpProxy = httpProxy;
 14         this.contentTransformer = contentTransformer;
 15     }
 16 
 
 17     public void render(HttpServletRequest request,  HttpServletResponse response)  throws IOException {
 18         String url = "http://plexrails.plexobject.com/guest_book/sign";
 19         ProxyState state = new ProxyState(url, MethodType.GET);
 
 20         String inputXhtml = httpProxy.request(state, null).getContents();
 21         Map<String, String> properties = new HashMap<String, String>();
 22         properties.put("callbackState", state.toExternalFormat());
 
 23         String transformedXhtml = contentTransformer.transform(inputXhtml, properties);
 24         response.getWriter().println(transformedXhtml);
 25     }
 26 
 27     public void submit(HttpServletRequest request,  HttpServletResponse response)  throws IOException {
 
 28         String originalActionUrl = request.getParameter("originalActionUrl");
 29         String orginalMethodType = request.getParameter("orginalMethodType");
 30         ProxyState userState = ProxyState.valueOf(request.getParameter("userState"));
 31         Map<String, String[]> params = request.getParameterMap();
 
 32         ProxyState state = new ProxyState(originalActionUrl, orginalMethodType);
 33         state.addCookies(userState.getCookies());
 34         ProxyResponse proxyResponse = httpProxy.request(state, params);
 35         response.getWriter().println(proxyResponse.getContents());
 36     }
 
 37 }
 38 
 39 
 

Download Code

You can download above code from here.

Acknowledgement

I would like to thank the folks at XSLT forum of Programmer-to-Programmer (http://p2p.wrox.com/forum.asp?FORUM_ID=79) for answering my XSLT questions.

Newer Posts »

Powered by WordPress