Thursday, June 28, 2012

On-Demand Object Injection with Guice in Jersey

In Java, you can create a new object instance by calling the new operator. It allows you to use an object instance on-demand, e.g. when a condition is met:
class Bar {
   void doSomething();
}
class Foo {
   void process() {
      boolean condition;
      // Do something and then check condition
      if (condition) {
         // Create a new Bar instance to do something 
         //only when condition is true
         Bar bar = new Bar();
         bar.doSomething();
      }
   }
}
In the example above, a new Bar instance is created on-demand when condition is true. But how would you do this in Guice when you are using Guice to "inject", a.k.a. create your objects? Guice is designed around the principle of eager dependency specification at the time of object construction. When an object Foo is created, all its dependendencies should have been "injected" by Guice during the object constrution phase. This kind of question is typical for a "framework" like Guice. A framework codifies a practice. Guice codifies the Factory pattern. But a framework often obfuscates idioms outside the codified pattern. So does Guice. How do you "new" an object Bar on-demand without first creating it in the constructor of the enclosing class Foo? It is actually quite easy in Guice. It is called "provider injection", i.e. injecting object factory. Guice automatically creates a provider for every object class that it injects. So assuming both Bar and Foo are injected by Guice like this:
import com.google.inject.AbstractModule;
class GuiceModule extends AbstractModule {
   @Override
   protected final void configure() {
      bind(Bar.class);
      bind(Foo.class);
   }
You can then inject a provider of Bar into Foo so you can ask Guice for a new instance of Bar whenenver you need it:
class Bar {
   void doSomething();
}

import com.google.inject.Provider;
class Foo {
   private final Provider<Bar> barProvider;

   @Inject
   Foo(final Provider<Bar> barProvider) {
      this.barProvider = barProvider;
   }
  
   void process() {
      boolean condition;
      // Do something and then check condition
      if (condition) {
         // Create a new Bar instance to do something 
         //only when condition is true
         Bar bar = barProvider.get();
         bar.doSomething();
      }
   }
}
This is the technique to use when you write sub-resource locators in Jersey with
Guice as the IoC container:
public class BarResource {
   @GET
   public Response get();
}

import com.google.inject.Provider;
@Path("/")
public class FooResource {
   private final Provider<BarResource> barProvider;

   @Inject
   FooResource(final Provider<BarResource> barProvider) {
      this.barProvider = barProvider;
   }
  
   @Path("bar")
   @Produces(MediaType.APPLICATION_JSON)
   public Response getBar() {
      // Client request /bar will will be redirected 
      //to BarResource
      BarResource bar = barProvider.get();
      bar.get();
   }
}

Monday, June 25, 2012

Poor Man's Static IP for EC2 a.k.a. Elastic Network Interface

Amamzon's Elastic Network Interface (EIN) allows you to "reserve" an IP address. This is immensely useful in VPC because an EIN can function as a pseudo static IP for elastic instances. Granted, you have to use two IPs for a single instance. But EIN lets you assign a fixed private IP address to an elastic instance without having to go through the trouble of setting up dynamic DNS update. Unfortunately, Amazon's documentation is missing key information on configurting secondary IP with EIN. Even if you have attached an EIN to an elastic instance, you cannot access the instance using the private IP associated with the EIN. What gives? The missing piece is IP interface and routing configuration. Below is a step-to-step guide to configure the EIN interface. This guide assumes that you have followed the official AWS guide to the point where you have configured an EIN and have brought up an elastic instance that is attached with that EIN. Further, it assumes that the primary interface is assigned an IP 10.3.1.190 and the secondary interface, which is the EIN, is assigned an IP 10.3.1.191. At the end of the exercise, we will be able to ssh to the secondary IP address in addition to the primary one.

First, check IP address binding to each network interface.

$ sudo ip address

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 02:26:69:f0:87:46 brd ff:ff:ff:ff:ff:ff
inet 10.3.1.190/24 brd 10.3.1.255 scope global eth0
inet6 fe80::26:69ff:fef0:8746/64 scope link
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
link/ether 02:26:69:dc:cc:62 brd ff:ff:ff:ff:ff:ff


We see from the output the current network interface assignment is of the following:

eth0: 10.3.1.190
eth1: none

Therefore,  the first order of business is to assign the EIN IP address to the interface eth1:

$ sudo ip address add 10.3.1.191/24 brd + dev eth1

Next, bring up the interface:

$ sudo ip link set dev eth1 up

Verify that eth1 is indeed up:

$ sudo ip address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 02:26:69:f0:87:46 brd ff:ff:ff:ff:ff:ff
inet 10.3.1.190/24 brd 10.3.1.255 scope global eth0
inet6 fe80::26:69ff:fef0:8746/64 scope link
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 02:26:69:dc:cc:62 brd ff:ff:ff:ff:ff:ff
inet 10.3.1.191/24 brd 10.3.1.255 scope global eth1
inet6 fe80::26:69ff:fedc:cc62/64 scope link
valid_lft forever preferred_lft forever


Next, find out the default gateway:

$ ip route show
default via 10.3.1.1 dev eth0
10.3.1.0/24 dev eth0 proto kernel scope link src 10.3.1.190
10.3.1.0/24 dev eth1 proto kernel scope link src 10.3.1.191


The default gateway is 10.3.1.1 in the output. It is bound to the virtual gateway associated with the VPC.  Since it is currently only bound to eth0, any traffic from eth1 that is destined to IP addresses outside the 10.3.1.0/24 IP block will be dropped! We need to reconfigure IP routing on the elastic instance to allow IP packets leaving eth1 to be routed through the default gateway. Here is how you do it.

First, add a new routing table called "awsein":

$ sudo echo 2 awsein >> /etc/iproute2/rt_tables

It will add a table called "awsein" to rt_tables as entry 2:
$ cat /etc/iproute2/rt_tables
#
# reserved values
#
255 local
254 main
253 default
0 unspec
#
# local
#
#1 inr.ruhep
2 awsein


Now adds a default route in the new table to use the same default gateway as the one used by eth0:

$ sudo ip route add default via 10.3.1.1 dev eth1 table awsein
$ sudo ip route flush cache

Confirm that the new route is indeed added:

$ ip route show table awsein
default via 10.3.1.1 dev eth1 metric 1000


Next, we need to create a new routing rule to trigger the default route on eth1 by its source IP. To do this, we first check existing routes:
$ ip rule
0: from all lookup local
32766: from all lookup main
32767: from all lookup default


Note the number 32766 for the rule "main". We will now add a new rule to "awsein" with a priority smaller than the one for "main".

$ sudo ip rule add from 10.3.1.191 lookup awsein prio 1000
Finally, verify the new rule configuration:

$ ip rule
0:      from all lookup local

1000:   from 10.3.1.191 lookup awsein
32766:  from all lookup main
32767:  from all lookup default

Now you can ssh into the instance using the EIN IP 10.3.1.191! Happy hacking.