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:
- Netbeans 6.9 or higher (download here)
- The ZK plugin (download here)
- Amazon AWS account (using the free tier you can stay even play for free). Login here
- The AWS Java SDK (download here)
Tutorial Part 1:
- Create a new ZK Web Application ‘ZKEC2CloudControl’
- Create a blank zul page ‘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’
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 - 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
You need to get the keys from your security credentials section in the 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:
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 InstanceIdFind EC2 instances
Most the EC2 methods from the SDK work similar
I reommend browsing through the API docs and the sample applications. - Run the 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.
