ZK goes EC2 (Part 1)

Certainly not a big deal to deploy a ZK web application to a Amazon EC2 instance, but I needed a simple application that allows my team to start/stop our EC2 instance that we use for testing and demo without logging into the AWS account or using the Firefox plugin (both giving too many rights and are too complex for some business users).
I created this app to give my users (tester and trainer) a chance to start the servers without logging into AWS. I share this because the are no samples in the SDK file that cover the EC2 instances in detail.

In part 1 of this tutorial we will create a ZK application that displays the status of our instances in a list.

In part 2 we will add the start-stop instance function, in part 3 we tinker with IP addresses and DynDNS domains and in part 4 we let our web application time scheduled (EJB Timer) control the instances automatically.

Pre-Requirements:

Tutorial Part 1:

  • Create a new ZK Web Application ‘ZKEC2CloudControl’

    New Web Application

    New Web Application

  • Create a blank zul page ‘instances.zul

    instances.zul

  • Create a simple screen to display our instances
    A listbox, a button and a label

    <?xml version="1.0" encoding="UTF-8"?>
    
    <zk xmlns="http://www.zkoss.org/2005/zul">
        <window id="list" apply="controller.instancesController" title="ZK EC2 CloudControl" width="100%">
            <listbox id="lstInstance" width="100%" >
                <listhead sizable="true">
                    <listheader  label="Instance ID"/>
                    <listheader label="Name"/>
                    <listheader  label="Public IP" />
                    <listheader  label="State" />
                    <listheader  label="Launch Time" />
                </listhead>
            </listbox>
            <button id="btnRefresh" label="Refresh" onClick=""/>
            <label id="lblStatus"/>
        </window>
    </zk>
    
    
  • Create the controller class ‘instancesController’

    instancesController class

    package controller;
    
    import org.zkoss.zk.ui.Component;
    import org.zkoss.zk.ui.util.ComposerExt;
    import org.zkoss.zk.ui.util.GenericForwardComposer;
    
    public class instancesController extends GenericForwardComposer implements ComposerExt {
    
        @Override
        public void doAfterCompose(Component comp) throws Exception {
            super.doAfterCompose(comp);
        }
    }
    
    

     

  • Add the AWS SDK and required 3rd party library files
    aws-java-sdk-1.2.10.jar
    aws-java-sdk-1.2.10-sources.jarFrom the third party folder (SDK):
    commons-codec-1.3.jar
    commons-logging-1.1.1.jar
    httpclient-4.1.1.jar
    httpcore-4.1.jar

    AWS SDK Libraries

    Add Libraies

  • Modify the controller class
    In the first step we only connect to EC2, read reservations and instances and render them in a zk listbox.
    The complete sourcecode first:

    package controller;
    
    import com.amazonaws.auth.BasicAWSCredentials;
    import com.amazonaws.services.ec2.AmazonEC2;
    import com.amazonaws.services.ec2.AmazonEC2Client;
    import com.amazonaws.services.ec2.model.DescribeInstancesResult;
    import com.amazonaws.services.ec2.model.Instance;
    import com.amazonaws.services.ec2.model.InstanceState;
    import com.amazonaws.services.ec2.model.Reservation;
    import com.amazonaws.services.ec2.model.Tag;
    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    import org.zkoss.zk.ui.Component;
    import org.zkoss.zk.ui.event.Event;
    import org.zkoss.zk.ui.util.ComposerExt;
    import org.zkoss.zk.ui.util.GenericForwardComposer;
    import org.zkoss.zul.Label;
    import org.zkoss.zul.ListModelList;
    import org.zkoss.zul.Listbox;
    import org.zkoss.zul.Listcell;
    import org.zkoss.zul.Listitem;
    import org.zkoss.zul.ListitemRenderer;
    
    public class instancesController extends GenericForwardComposer implements ComposerExt {
    
        // ZK Variables
        private Listbox lstInstance;
        private Label lblStatus;
    
        //Amazon Variables
        AmazonEC2 ec2;
        List<Reservation> listEC2Reservations = null;
        List<Instance> listEC2Instances = null;
    
        private void initEC2() {
            BasicAWSCredentials ecProp = new BasicAWSCredentials("YOURKEY", "YOURSECRETKEY");
            ec2 = new AmazonEC2Client(ecProp);
            ec2.setEndpoint("ec2.ap-southeast-1.amazonaws.com");
        }
    
        @Override
        public void doAfterCompose(Component comp) throws Exception {
            super.doAfterCompose(comp);
    
            initEC2();
            setupRenderer();
            listReservationsInstances();
    
        }
    
        private void listReservationsInstances() {
    
            DescribeInstancesResult describeInstancesRequest = ec2.describeInstances();
            listEC2Reservations = describeInstancesRequest.getReservations();
    
            Set<Instance> instances = new HashSet<Instance>();
            for (Reservation reservation : listEC2Reservations) {
                instances.addAll(reservation.getInstances());
            }
            listEC2Instances = new ArrayList<Instance>(instances);
    
            lstInstance.setModel(new ListModelList(listEC2Instances));
    
        }
    
        public void onClick$btnRefresh(Event evt) throws InterruptedException {
            listReservationsInstances();
        }
    
        private void setupRenderer() {
    
            // EC2 Instances list renderer
            ListitemRenderer listRenderInstance = new ListitemRenderer() {
    
                @Override
                public void render(Listitem item, Object data) throws Exception {
                    item.setValue(data);
                    item.appendChild(new Listcell(((Instance) data).getInstanceId()));
                    item.appendChild(new Listcell(findTagValuebyKey(((Instance) data).getTags(), "name")));
                    item.appendChild(new Listcell(((Instance) data).getPublicIpAddress()));
    
                    InstanceState state = ((Instance) data).getState();
                    item.appendChild(new Listcell(state.getName()));
    
                    item.appendChild(new Listcell(((Instance) data).getLaunchTime().toString()));
    
                    Listcell listcell = new Listcell();
                    item.appendChild(listcell);
                }
            };
            lstInstance.setItemRenderer(listRenderInstance);
        }
    
        private String findTagValuebyKey(List<Tag> tags, String key) {
    
            for (Tag tag : tags) {
                if (tag.getKey().toUpperCase().equals(key.toUpperCase())) {
                    return tag.getValue();
                }
            }
            return "na";
        }
    }
    
    

    Few comments on the sourcecode

    EC2 Access Keys and Endpoint

    Access Keys

    You need to get the keys from your security credentials section in the AWS console

    AWS Console

    You need to set the endpoint according to the location of your ec2 instances. In a more sophisticated version you could make this configurable instead of hardcoded. Currently (October 2011) available endpoints:

    EC2 endpoints

    Customer Listrenderer for the listbox
    Using the listmodel which is direcly bound the list that you get back from EC2 call, we need to create a customer renderer to display the data for each column
    Sample: ..((Instance) data.getInstanceId()).. Cast object data to Amazon EC2 instance type and read the InstanceId

    Renderer

    Find EC2 instances
    Most the EC2 methods from the SDK work similar
    I reommend browsing through the API docs and the sample applications.

    EC2 methods

  • Run the application

    Running Web Application

    One caveat: The listbox shows a random sequence list of your instances due to the response from the API call. Need to implement a sort.

Stay tuned for the next parts where we add functionality to the application.

Advertisements

One thought on “ZK goes EC2 (Part 1)

  1. Pingback: ZK goes EC2 (Part 2) « The JavaDude Weblog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s