Index: src/main/java/hudson/plugins/ec2/EC2Cloud.java
===================================================================
--- src/main/java/hudson/plugins/ec2/EC2Cloud.java	(revision 29721)
+++ src/main/java/hudson/plugins/ec2/EC2Cloud.java	(working copy)
@@ -1,490 +1,68 @@
 package hudson.plugins.ec2;
 
-import com.xerox.amazonws.ec2.EC2Exception;
-import com.xerox.amazonws.ec2.InstanceType;
-import com.xerox.amazonws.ec2.Jec2;
-import com.xerox.amazonws.ec2.KeyPairInfo;
-import com.xerox.amazonws.ec2.ReservationDescription;
-import com.xerox.amazonws.ec2.ReservationDescription.Instance;
-import hudson.model.Computer;
-import hudson.model.Descriptor;
-import hudson.model.Hudson;
-import hudson.model.Label;
-import hudson.model.Node;
-import hudson.slaves.Cloud;
-import hudson.slaves.NodeProvisioner.PlannedNode;
+import hudson.Extension;
 import hudson.util.FormValidation;
-import hudson.util.Secret;
-import hudson.util.StreamTaskListener;
-import java.net.MalformedURLException;
-
-import org.jets3t.service.Constants;
-import org.jets3t.service.S3Service;
-import org.jets3t.service.S3ServiceException;
-import org.jets3t.service.impl.rest.httpclient.RestS3Service;
-import org.jets3t.service.security.AWSCredentials;
-import org.jets3t.service.utils.ServiceUtils;
+import org.kohsuke.stapler.DataBoundConstructor;
 import org.kohsuke.stapler.QueryParameter;
-import org.kohsuke.stapler.StaplerRequest;
 import org.kohsuke.stapler.StaplerResponse;
 
 import javax.servlet.ServletException;
-import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.StringReader;
-import java.io.StringWriter;
+import java.net.MalformedURLException;
 import java.net.URL;
-import java.net.URLEncoder;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.logging.Logger;
-
-import org.jets3t.service.Jets3tProperties;
-import static java.util.logging.Level.WARNING;
 
 /**
- * Hudson's view of EC2. 
+ * An Amazon EC2 Cloud.
+ * The original implementation of {@link GenericEC2Cloud}.
  *
  * @author Kohsuke Kawaguchi
  */
-public abstract class EC2Cloud extends Cloud {
-
-    private final String accessId;
-    private final Secret secretKey;
-    private final EC2PrivateKey privateKey;
-
-    /**
-     * Upper bound on how many instances we may provision.
-     */
-    public final int instanceCap;
-    private final List<SlaveTemplate> templates;
-    private transient KeyPairInfo usableKeyPair;
-
-    protected EC2Cloud(String id, String accessId, String secretKey, String privateKey, String instanceCapStr, List<SlaveTemplate> templates) {
-        super(id);
-        this.accessId = accessId.trim();
-        this.secretKey = Secret.fromString(secretKey.trim());
-        this.privateKey = new EC2PrivateKey(privateKey);
-        if(instanceCapStr.equals(""))
-            this.instanceCap = Integer.MAX_VALUE;
-        else
-            this.instanceCap = Integer.parseInt(instanceCapStr);
-        if(templates==null)     templates=Collections.emptyList();
-        this.templates = templates;
-        readResolve(); // set parents
-    }
-
-    public abstract URL getEc2EndpointUrl() throws IOException;
-    public abstract URL getS3EndpointUrl() throws IOException;
-
-    protected Object readResolve() {
-        for (SlaveTemplate t : templates)
-            t.parent = this;
-        return this;
-    }
-
-    public String getAccessId() {
-        return accessId;
-    }
-
-    public String getSecretKey() {
-        return secretKey.getEncryptedValue();
-    }
-
-    public EC2PrivateKey getPrivateKey() {
-        return privateKey;
-    }
-
-    public String getInstanceCapStr() {
-        if(instanceCap==Integer.MAX_VALUE)
-            return "";
-        else
-            return String.valueOf(instanceCap);
-    }
-
-    public List<SlaveTemplate> getTemplates() {
-        return Collections.unmodifiableList(templates);
-    }
-
-    public SlaveTemplate getTemplate(String ami) {
-        for (SlaveTemplate t : templates)
-            if(t.ami.equals(ami))
-                return t;
-        return null;
-    }
-
-    /**
-     * Gets {@link SlaveTemplate} that has the matching {@link Label}.
-     */
-    public SlaveTemplate getTemplate(Label label) {
-        for (SlaveTemplate t : templates)
-            if(t.containsLabel(label))
-                return t;
-        return null;
-    }
-
-    /**
-     * Gets the {@link KeyPairInfo} used for the launch.
-     */
-    public synchronized KeyPairInfo getKeyPair() throws EC2Exception, IOException {
-        if(usableKeyPair==null)
-            usableKeyPair = privateKey.find(connect());
-        return usableKeyPair;
-    }
-
-    /**
-     * Counts the number of instances in EC2 currently running.
-     *
-     * <p>
-     * This includes those instances that may be started outside Hudson.
-     */
-    public int countCurrentEC2Slaves() throws EC2Exception {
-        int n=0;
-        for (ReservationDescription r : connect().describeInstances(Collections.<String>emptyList())) {
-            for (Instance i : r.getInstances()) {
-                if(!i.isTerminated())
-                    n++;
-            }
-        }
-        return n;
-    }
-
-    /**
-     * Debug command to attach to a running instance.
-     */
-    public void doAttach(StaplerRequest req, StaplerResponse rsp, @QueryParameter String id) throws ServletException, IOException, EC2Exception {
-        checkPermission(PROVISION);
-        SlaveTemplate t = getTemplates().get(0);
-
-        StringWriter sw = new StringWriter();
-        StreamTaskListener listener = new StreamTaskListener(sw);
-        EC2Slave node = t.attach(id,listener);
-        Hudson.getInstance().addNode(node);
-
-        rsp.sendRedirect2(req.getContextPath()+"/computer/"+node.getNodeName());
-    }
-
-    public void doProvision(StaplerRequest req, StaplerResponse rsp, @QueryParameter String ami) throws ServletException, IOException {
-        checkPermission(PROVISION);
-        if(ami==null) {
-            sendError("The 'ami' query parameter is missing",req,rsp);
-            return;
-        }
-        SlaveTemplate t = getTemplate(ami);
-        if(t==null) {
-            sendError("No such AMI: "+ami,req,rsp);
-            return;
-        }
-
-        StringWriter sw = new StringWriter();
-        StreamTaskListener listener = new StreamTaskListener(sw);
-        try {
-            EC2Slave node = t.provision(listener);
-            Hudson.getInstance().addNode(node);
-
-            rsp.sendRedirect2(req.getContextPath()+"/computer/"+node.getNodeName());
-        } catch (EC2Exception e) {
-            e.printStackTrace(listener.error(e.getMessage()));
-            sendError(sw.toString(),req,rsp);
-        }
-    }
-
-    public Collection<PlannedNode> provision(Label label, int excessWorkload) {
-        try {
-
-            final SlaveTemplate t = getTemplate(label);
-
-            List<PlannedNode> r = new ArrayList<PlannedNode>();
-            for( ; excessWorkload>0; excessWorkload-- ) {
-                if(countCurrentEC2Slaves()>=instanceCap)
-                    break;      // maxed out
-
-                r.add(new PlannedNode(t.getDisplayName(),
-                        Computer.threadPoolForRemoting.submit(new Callable<Node>() {
-                            public Node call() throws Exception {
-                                // TODO: record the output somewhere
-                                EC2Slave s = t.provision(new StreamTaskListener(System.out));
-                                Hudson.getInstance().addNode(s);
-                                // EC2 instances may have a long init script. If we declare
-                                // the provisioning complete by returning without the connect
-                                // operation, NodeProvisioner may decide that it still wants
-                                // one more instance, because it sees that (1) all the slaves
-                                // are offline (because it's still being launched) and
-                                // (2) there's no capacity provisioned yet.
-                                //
-                                // deferring the completion of provisioning until the launch
-                                // goes successful prevents this problem.
-                                s.toComputer().connect(false).get();
-                                return s;
-                            }
-                        })
-                        ,t.getNumExecutors()));
-            }
-            return r;
-        } catch (EC2Exception e) {
-            LOGGER.log(WARNING,"Failed to count the # of live instances on EC2",e);
-            return Collections.emptyList();
-        }
-    }
-
-    public boolean canProvision(Label label) {
-        return getTemplate(label)!=null;
-    }
-
+public class EC2Cloud extends GenericEC2Cloud {
     /**
-     * Gets the first {@link EC2Cloud} instance configured in the current Hudson, or null if no such thing exists.
+     * Represents the region. Can be null for backward compatibility reasons.
      */
-    public static EC2Cloud get() {
-        return Hudson.getInstance().clouds.get(EC2Cloud.class);
-    }
+    private AwsRegion region;
 
-    /**
-     * Connects to EC2 and returns {@link Jec2}, which can then be used to communicate with EC2.
-     */
-    public Jec2 connect() throws EC2Exception {
-        try {
-            return connect(accessId, secretKey, getEc2EndpointUrl());
-        } catch (IOException e) {
-            throw new EC2Exception("Failed to retrieve the endpoint",e);
-        }
+    @DataBoundConstructor
+    public EC2Cloud(AwsRegion region, String accessId, String secretKey, String privateKey, String instanceCapStr, List<SlaveTemplate> templates) {
+        super("ec2-"+region.name(), accessId, secretKey, privateKey, instanceCapStr, templates);
+        this.region = region;
     }
 
-    /***
-     * Connect to an EC2 instance.
-     * @return Jec2
-     */
-    public static Jec2 connect(String accessId, String secretKey, URL endpoint) {
-        return connect(accessId, Secret.fromString(secretKey), endpoint);
+    public AwsRegion getRegion() {
+        if (region==null)
+            region = AwsRegion.US_EAST_1; // backward data compatibility with earlier versions
+        return region;
     }
 
-    /***
-     * Connect to an EC2 instance.
-     * @return Jec2
-     */
-    public static Jec2 connect(String accessId, Secret secretKey, URL endpoint) {
-        int ec2Port = portFromURL(endpoint);
-        boolean SSL = isSSL(endpoint);
-        Jec2 result = new Jec2(accessId, secretKey.toString(), SSL, endpoint.getHost(), ec2Port);
-        String path = endpoint.getPath();
-        if (path.length() != 0) /* '/' is the default, not '' */
-            result.setResourcePrefix(path);
-        return result;
-    }
-
-    /***
-     * Convert a configured hostname like 'us-east-1' to a FQDN or ip address
-     */
-    public static String convertHostName(String ec2HostName) {
-        if (ec2HostName == null || ec2HostName.length()==0)
-            ec2HostName = "us-east-1";
-        if (!ec2HostName.contains("."))
-            ec2HostName = ec2HostName + ".ec2.amazonaws.com";
-	return ec2HostName;
-    }
-
-    /***
-     * Convert a configured s3 endpoint to a FQDN or ip address
-     */
-    public static String convertS3HostName(String s3HostName) {
-        if (s3HostName == null || s3HostName.length()==0)
-            s3HostName = "s3";
-        if (!s3HostName.contains("."))
-            s3HostName = s3HostName + ".amazonaws.com";
-	return s3HostName;
-    }
-
-    /***
-     * Convert a user entered string into a port number
-     * "" -> -1 to indicate default based on SSL setting
-     */
-    public static Integer convertPort(String ec2Port) {
-        if (ec2Port == null || ec2Port.length() == 0)
-            return -1;
-        else
-            return Integer.parseInt(ec2Port);
-    }
-
-    /**
-     * Connects to S3 and returns {@link S3Service}.
-     */
-    public S3Service connectS3() throws S3ServiceException, IOException {
-        URL s3 = getS3EndpointUrl();
-
-        return new RestS3Service(new AWSCredentials(accessId,secretKey.toString()),
-            null, null, buildJets3tProperties(s3));
-    }
-
-    /**
-     * Builds the connection parameters for S3.
-     */
-    protected Jets3tProperties buildJets3tProperties(URL s3) {
-        Jets3tProperties props = Jets3tProperties.getInstance(Constants.JETS3T_PROPERTIES_FILENAME);
-        final String s3Host = s3.getHost();
-        if (!s3Host.equals("s3.amazonaws.com"))
-            props.setProperty("s3service.s3-endpoint", s3Host);
-        int s3Port = portFromURL(s3);
-        if (s3Port != -1)
-            props.setProperty("s3service.s3-endpoint-http-port", String.valueOf(s3Port));
-        if (s3.getPath().length() > 1)
-            props.setProperty("s3service.s3-endpoint-virtual-path", s3.getPath());
-        props.setProperty("s3service.https-only", String.valueOf(isSSL(s3)));
-        return props;
-    }
-
-    /**
-     * Computes the presigned URL for the given S3 resource.
-     *
-     * @param path
-     *      String like "/bucketName/folder/folder/abc.txt" that represents the resource to request.
-     */
-    public URL buildPresignedURL(String path) throws IOException, S3ServiceException {
-        long expires = System.currentTimeMillis()/1000+60*60;
-        String token = "GET\n\n\n" + expires + "\n" + path;
-
-        String url = "http://s3.amazonaws.com"+path+"?AWSAccessKeyId="+accessId+"&Expires="+expires+"&Signature="+
-                URLEncoder.encode(
-                        ServiceUtils.signWithHmacSha1(secretKey.toString(),token),"UTF-8");
-        return new URL(url);
+    @Override
+    public URL getEc2EndpointUrl() {
+        return getRegion().ec2Endpoint;
     }
 
-    /* Parse a url or return a sensible error */
-    public static URL checkEndPoint(String url) throws FormValidation {
-        try {
-            return new URL(url);
-        } catch (MalformedURLException ex) {
-            throw FormValidation.error("Endpoint URL is not a valid URL");
-        }
+    @Override
+    public URL getS3EndpointUrl() {
+        return getRegion().s3Endpoint;
     }
 
-
-    public static abstract class DescriptorImpl extends Descriptor<Cloud> {
-        public InstanceType[] getInstanceTypes() {
-            return InstanceType.values();
-        }
-
-        /**
-         * TODO: once 1.304 is released, revert to FormValidation.validateBase64
-         */
-        private FormValidation validateBase64(String value, boolean allowWhitespace, boolean allowEmpty, String errorMessage) {
-            try {
-                String v = value;
-                if(!allowWhitespace) {
-                    if(v.indexOf(' ')>=0 || v.indexOf('\n')>=0)
-                        return FormValidation.error(errorMessage);
-                }
-                v=v.trim();
-                if(!allowEmpty && v.length()==0)
-                    return FormValidation.error(errorMessage);
-
-                com.trilead.ssh2.crypto.Base64.decode(v.toCharArray());
-                return FormValidation.ok();
-            } catch (IOException e) {
-                return FormValidation.error(errorMessage);
-            }
+    @Extension
+    public static class DescriptorImpl extends GenericEC2Cloud.DescriptorImpl {
+        public String getDisplayName() {
+            return "Amazon EC2";
         }
 
-        public FormValidation doCheckAccessId(@QueryParameter String value) throws IOException, ServletException {
-            return validateBase64(value,false,false,Messages.EC2Cloud_InvalidAccessId());
+        public FormValidation doTestConnection(
+                 @QueryParameter AwsRegion region,
+                 @QueryParameter String accessId,
+                 @QueryParameter String secretKey,
+                 @QueryParameter String privateKey) throws IOException, ServletException {
+            return super.doTestConnection(region.ec2Endpoint,accessId,secretKey,privateKey);
         }
 
-        public FormValidation doCheckSecretKey(@QueryParameter String value) throws IOException, ServletException {
-            return validateBase64(value,false,false,Messages.EC2Cloud_InvalidSecretKey());
+        public FormValidation doGenerateKey(
+                StaplerResponse rsp, @QueryParameter AwsRegion region, @QueryParameter String accessId, @QueryParameter String secretKey) throws IOException, ServletException {
+            return super.doGenerateKey(rsp,region.ec2Endpoint,accessId,secretKey);
         }
-
-        public FormValidation doCheckPrivateKey(@QueryParameter String value) throws IOException, ServletException {
-            boolean hasStart=false,hasEnd=false;
-            BufferedReader br = new BufferedReader(new StringReader(value));
-            String line;
-            while ((line = br.readLine()) != null) {
-                if (line.equals("-----BEGIN RSA PRIVATE KEY-----"))
-                    hasStart=true;
-                if (line.equals("-----END RSA PRIVATE KEY-----"))
-                    hasEnd=true;
-            }
-            if(!hasStart)
-                return FormValidation.error("This doesn't look like a private key at all");
-            if(!hasEnd)
-                return FormValidation.error("The private key is missing the trailing 'END RSA PRIVATE KEY' marker. Copy&paste error?");
-            return FormValidation.ok();
-        }
-
-        protected FormValidation doTestConnection( URL ec2endpoint,
-                                     String accessId, String secretKey, String privateKey) throws IOException, ServletException {
-            try {
-                Jec2 jec2 = connect(accessId, secretKey, ec2endpoint);
-                jec2.describeInstances(Collections.<String>emptyList());
-
-                if(accessId==null)
-                    return FormValidation.error("Access ID is not specified");
-                if(secretKey==null)
-                    return FormValidation.error("Secret key is not specified");
-                if(privateKey==null)
-                    return FormValidation.error("Private key is not specified. Click 'Generate Key' to generate one.");
-
-                if(privateKey.trim().length()>0) {
-                    // check if this key exists
-                    EC2PrivateKey pk = new EC2PrivateKey(privateKey);
-                    if(pk.find(jec2)==null)
-                        return FormValidation.error("The private key entered below isn't registered to EC2 (fingerprint is "+pk.getFingerprint()+")");
-                }
-
-                return FormValidation.ok(Messages.EC2Cloud_Success());
-            } catch (EC2Exception e) {
-                LOGGER.log(WARNING, "Failed to check EC2 credential",e);
-                return FormValidation.error(e.getMessage());
-            }
-        }
-
-        public FormValidation doGenerateKey(StaplerResponse rsp, URL ec2EndpointUrl, String accessId, String secretKey
-        ) throws IOException, ServletException {
-            try {
-                Jec2 jec2 = connect(accessId, secretKey, ec2EndpointUrl);
-                List<KeyPairInfo> existingKeys = jec2.describeKeyPairs(Collections.<String>emptyList());
-
-                int n = 0;
-                while(true) {
-                    boolean found = false;
-                    for (KeyPairInfo k : existingKeys) {
-                        if(k.getKeyName().equals("hudson-"+n))
-                            found=true;
-                    }
-                    if(!found)
-                        break;
-                    n++;
-                }
-
-                KeyPairInfo key = jec2.createKeyPair("hudson-" + n);
-
-
-                rsp.addHeader("script","findPreviousFormItem(button,'privateKey').value='"+key.getKeyMaterial().replace("\n","\\n")+"'");
-
-                return FormValidation.ok(Messages.EC2Cloud_Success());
-            } catch (EC2Exception e) {
-                LOGGER.log(WARNING, "Failed to check EC2 credential",e);
-                return FormValidation.error(e.getMessage());
-            }
-        }
-    }
-
-    private static final Logger LOGGER = Logger.getLogger(EC2Cloud.class.getName());
-
-    private static boolean isSSL(URL endpoint) {
-        return endpoint.getProtocol().equals("https");
-    }
-
-    private static int portFromURL(URL endpoint) {
-        int ec2Port = endpoint.getPort();
-        if (ec2Port == -1) {
-            ec2Port = endpoint.getDefaultPort();
-        }
-        return ec2Port;
-    }
-
-    static {
-        // backward compatibility. EC2Cloud used to be a concrete class that represents AmazonEC2Cloud
-        Hudson.XSTREAM.alias(EC2Cloud.class.getName(),AmazonEC2Cloud.class);
     }
 }
Index: src/main/java/hudson/plugins/ec2/SlaveTemplate.java
===================================================================
--- src/main/java/hudson/plugins/ec2/SlaveTemplate.java	(revision 29721)
+++ src/main/java/hudson/plugins/ec2/SlaveTemplate.java	(working copy)
@@ -43,7 +43,7 @@
     public final String numExecutors;
     public final String remoteAdmin;
     public final String rootCommandPrefix;
-    protected transient EC2Cloud parent;
+    protected transient GenericEC2Cloud parent;
 
     private transient /*almost final*/ Set<Label> labelSet;
 
@@ -62,7 +62,7 @@
         readResolve(); // initialize
     }
     
-    public EC2Cloud getParent() {
+    public GenericEC2Cloud getParent() {
         return parent;
     }
 
@@ -177,7 +177,7 @@
                 @QueryParameter String accessId, @QueryParameter String secretKey,
                 @QueryParameter String ec2EndpointUrl,
                 final @QueryParameter String ami) throws IOException, ServletException {
-            Jec2 jec2 = EC2Cloud.connect(accessId, secretKey, EC2Cloud.checkEndPoint(ec2EndpointUrl));
+            Jec2 jec2 = GenericEC2Cloud.connect(accessId, secretKey, GenericEC2Cloud.checkEndPoint(ec2EndpointUrl));
             if(jec2!=null) {
                 try {
                     List<String> images = new LinkedList<String>();
Index: src/main/java/hudson/plugins/ec2/EC2Slave.java
===================================================================
--- src/main/java/hudson/plugins/ec2/EC2Slave.java	(revision 29721)
+++ src/main/java/hudson/plugins/ec2/EC2Slave.java	(working copy)
@@ -82,7 +82,7 @@
      */
     public void terminate() {
         try {
-            Jec2 ec2 = EC2Cloud.get().connect();
+            Jec2 ec2 = GenericEC2Cloud.get().connect();
             ec2.terminateInstances(Collections.singletonList(getInstanceId()));
             LOGGER.info("Terminated EC2 instance: "+getInstanceId());
             Hudson.getInstance().removeNode(this);
Index: src/main/java/hudson/plugins/ec2/Eucalyptus.java
===================================================================
--- src/main/java/hudson/plugins/ec2/Eucalyptus.java	(revision 29721)
+++ src/main/java/hudson/plugins/ec2/Eucalyptus.java	(working copy)
@@ -34,7 +34,7 @@
  *
  * @author Kohsuke Kawaguchi
  */
-public class Eucalyptus extends EC2Cloud {
+public class Eucalyptus extends GenericEC2Cloud {
     private transient Metadata metadata;
 
     public final URL url;
@@ -72,7 +72,7 @@
     }
 
     @Extension
-    public static class DescriptorImpl extends EC2Cloud.DescriptorImpl {
+    public static class DescriptorImpl extends GenericEC2Cloud.DescriptorImpl {
         public String getDisplayName() {
             return "Eucalyptus";
         }
Index: src/main/java/hudson/plugins/ec2/EC2Computer.java
===================================================================
--- src/main/java/hudson/plugins/ec2/EC2Computer.java	(revision 29721)
+++ src/main/java/hudson/plugins/ec2/EC2Computer.java	(working copy)
@@ -39,7 +39,7 @@
      * Gets the EC2 console output.
      */
     public String getConsoleOutput() throws EC2Exception {
-        Jec2 ec2 = EC2Cloud.get().connect();
+        Jec2 ec2 = GenericEC2Cloud.get().connect();
         return ec2.getConsoleOutput(getInstanceId()).getOutput();
     }
 
@@ -82,7 +82,7 @@
     }
 
     private ReservationDescription.Instance _describeInstance() throws EC2Exception {
-        return EC2Cloud.get().connect().describeInstances(Collections.<String>singletonList(getNode().getInstanceId())).get(0).getInstances().get(0);
+        return GenericEC2Cloud.get().connect().describeInstances(Collections.<String>singletonList(getNode().getInstanceId())).get(0).getInstances().get(0);
     }
 
     /**
Index: src/main/java/hudson/plugins/ec2/GenericEC2Cloud.java
===================================================================
--- src/main/java/hudson/plugins/ec2/GenericEC2Cloud.java	(revision 29721)
+++ src/main/java/hudson/plugins/ec2/GenericEC2Cloud.java	(working copy)
@@ -50,7 +50,7 @@
  *
  * @author Kohsuke Kawaguchi
  */
-public abstract class EC2Cloud extends Cloud {
+public abstract class GenericEC2Cloud extends Cloud {
 
     private final String accessId;
     private final Secret secretKey;
@@ -63,7 +63,7 @@
     private final List<SlaveTemplate> templates;
     private transient KeyPairInfo usableKeyPair;
 
-    protected EC2Cloud(String id, String accessId, String secretKey, String privateKey, String instanceCapStr, List<SlaveTemplate> templates) {
+    protected GenericEC2Cloud(String id, String accessId, String secretKey, String privateKey, String instanceCapStr, List<SlaveTemplate> templates) {
         super(id);
         this.accessId = accessId.trim();
         this.secretKey = Secret.fromString(secretKey.trim());
@@ -235,10 +235,10 @@
     }
 
     /**
-     * Gets the first {@link EC2Cloud} instance configured in the current Hudson, or null if no such thing exists.
+     * Gets the first {@link GenericEC2Cloud} instance configured in the current Hudson, or null if no such thing exists.
      */
-    public static EC2Cloud get() {
-        return Hudson.getInstance().clouds.get(EC2Cloud.class);
+    public static GenericEC2Cloud get() {
+        return Hudson.getInstance().clouds.get(GenericEC2Cloud.class);
     }
 
     /**
@@ -469,7 +469,7 @@
         }
     }
 
-    private static final Logger LOGGER = Logger.getLogger(EC2Cloud.class.getName());
+    private static final Logger LOGGER = Logger.getLogger(GenericEC2Cloud.class.getName());
 
     private static boolean isSSL(URL endpoint) {
         return endpoint.getProtocol().equals("https");
@@ -482,9 +482,4 @@
         }
         return ec2Port;
     }
-
-    static {
-        // backward compatibility. EC2Cloud used to be a concrete class that represents AmazonEC2Cloud
-        Hudson.XSTREAM.alias(EC2Cloud.class.getName(),AmazonEC2Cloud.class);
-    }
 }
Index: src/main/java/hudson/plugins/ec2/AmazonEC2Cloud.java
===================================================================
--- src/main/java/hudson/plugins/ec2/AmazonEC2Cloud.java	(revision 29721)
+++ src/main/java/hudson/plugins/ec2/AmazonEC2Cloud.java	(working copy)
@@ -1,67 +0,0 @@
-package hudson.plugins.ec2;
-
-import hudson.Extension;
-import hudson.util.FormValidation;
-import org.kohsuke.stapler.DataBoundConstructor;
-import org.kohsuke.stapler.QueryParameter;
-import org.kohsuke.stapler.StaplerResponse;
-
-import javax.servlet.ServletException;
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.List;
-
-/**
- * The original implementation of {@link EC2Cloud}.
- *
- * @author Kohsuke Kawaguchi
- */
-public class AmazonEC2Cloud extends EC2Cloud {
-    /**
-     * Represents the region. Can be null for backward compatibility reasons.
-     */
-    private AwsRegion region;
-
-    @DataBoundConstructor
-    public AmazonEC2Cloud(AwsRegion region, String accessId, String secretKey, String privateKey, String instanceCapStr, List<SlaveTemplate> templates) {
-        super("ec2-"+region.name(), accessId, secretKey, privateKey, instanceCapStr, templates);
-        this.region = region;
-    }
-
-    public AwsRegion getRegion() {
-        if (region==null)
-            region = AwsRegion.US_EAST_1; // backward data compatibility with earlier versions
-        return region;
-    }
-
-    @Override
-    public URL getEc2EndpointUrl() {
-        return getRegion().ec2Endpoint;
-    }
-
-    @Override
-    public URL getS3EndpointUrl() {
-        return getRegion().s3Endpoint;
-    }
-
-    @Extension
-    public static class DescriptorImpl extends EC2Cloud.DescriptorImpl {
-        public String getDisplayName() {
-            return "Amazon EC2";
-        }
-
-        public FormValidation doTestConnection(
-                 @QueryParameter AwsRegion region,
-                 @QueryParameter String accessId,
-                 @QueryParameter String secretKey,
-                 @QueryParameter String privateKey) throws IOException, ServletException {
-            return super.doTestConnection(region.ec2Endpoint,accessId,secretKey,privateKey);
-        }
-
-        public FormValidation doGenerateKey(
-                StaplerResponse rsp, @QueryParameter AwsRegion region, @QueryParameter String accessId, @QueryParameter String secretKey) throws IOException, ServletException {
-            return super.doGenerateKey(rsp,region.ec2Endpoint,accessId,secretKey);
-        }
-    }
-}
Index: src/main/java/hudson/plugins/ec2/ssh/EC2UnixLauncher.java
===================================================================
--- src/main/java/hudson/plugins/ec2/ssh/EC2UnixLauncher.java	(revision 29721)
+++ src/main/java/hudson/plugins/ec2/ssh/EC2UnixLauncher.java	(working copy)
@@ -9,7 +9,7 @@
 import com.xerox.amazonws.ec2.ReservationDescription.Instance;
 import hudson.model.Descriptor;
 import hudson.model.Hudson;
-import hudson.plugins.ec2.EC2Cloud;
+import hudson.plugins.ec2.GenericEC2Cloud;
 import hudson.plugins.ec2.EC2Computer;
 import hudson.plugins.ec2.EC2ComputerLauncher;
 import hudson.remoting.Channel;
@@ -50,7 +50,7 @@
             else {
                 // connect fresh as ROOT
                 cleanupConn = connectToSsh(inst);
-                KeyPairInfo key = EC2Cloud.get().getKeyPair();
+                KeyPairInfo key = GenericEC2Cloud.get().getKeyPair();
                 if (!cleanupConn.authenticateWithPublicKey("root", key.getKeyMaterial().toCharArray(), "")) {
                     logger.println("Authentication failed");
                     return; // failed to connect as root.
@@ -91,7 +91,7 @@
                 String jdk = "java1.6.0_12";
                 String path = "/hudson-ci/jdk/linux-i586/" + jdk + ".tgz";
 
-                URL url = EC2Cloud.get().buildPresignedURL(path);
+                URL url = GenericEC2Cloud.get().buildPresignedURL(path);
                 if(conn.exec("wget -nv -O /usr/" + jdk + ".tgz '" + url + "'", logger) !=0) {
                     logger.println("Failed to download Java");
                     return;
@@ -136,7 +136,7 @@
         try {
             int tries = 20;
             boolean isAuthenticated = false;
-            KeyPairInfo key = EC2Cloud.get().getKeyPair();
+            KeyPairInfo key = GenericEC2Cloud.get().getKeyPair();
             while (tries-- > 0) {
                 isAuthenticated = bootstrapConn.authenticateWithPublicKey(computer.getRemoteAdmin(), key.getKeyMaterial().toCharArray(), "");
                 if (isAuthenticated) {
Index: src/main/resources/hudson/plugins/ec2/EC2Cloud/config-entries.jelly
===================================================================
--- src/main/resources/hudson/plugins/ec2/EC2Cloud/config-entries.jelly	(revision 0)
+++ src/main/resources/hudson/plugins/ec2/EC2Cloud/config-entries.jelly	(revision 0)
@@ -0,0 +1,21 @@
+<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
+  <f:entry title="${%Region}" field="region">
+    <f:enum>${it.displayName}</f:enum>
+  </f:entry>
+  <f:entry title="${%Access Key ID}" field="accessId">
+    <f:textbox />
+  </f:entry>
+  <f:entry title="${%Secret Access Key}" field="secretKey">
+    <f:password />
+  </f:entry>
+  <f:entry title="${%EC2 RSA Private Key}" field="privateKey">
+    <f:textarea />
+  </f:entry>
+  <f:advanced>
+    <f:entry title="${%Instance Cap}" field="instanceCapStr">
+      <f:textbox />
+    </f:entry>
+  </f:advanced>
+  <f:validateButton title="${%Generate Key}" progress="${%Generate...}" method="generateKey" with="region,secretKey,accessId" />
+  <f:validateButton title="${%Test Connection}" progress="${%Testing...}" method="testConnection" with="region,secretKey,accessId,privateKey" />
+</j:jelly>
Index: src/main/resources/hudson/plugins/ec2/EC2Cloud/help-region.html
===================================================================
--- src/main/resources/hudson/plugins/ec2/EC2Cloud/help-region.html	(revision 0)
+++ src/main/resources/hudson/plugins/ec2/EC2Cloud/help-region.html	(revision 0)
@@ -0,0 +1,6 @@
+<div>
+    Specifies the geographic region in which your slaves will run. Pick the region closest to you. 
+    Regions can be thought of as
+    independent instances of EC2/S3. See <a href="http://support.rightscale.com/index.php?title=2._References/02-Cloud_Infrastructures/01-Amazon_Web_Services_(AWS)/02-Amazon_EC2/EC2_Regions:__EC2-US_%26_EC2-EU">online resources</a>
+    for more about what regions are.
+</div>
\ No newline at end of file
Index: src/main/resources/hudson/plugins/ec2/AmazonEC2Cloud/config-entries.jelly
===================================================================
--- src/main/resources/hudson/plugins/ec2/AmazonEC2Cloud/config-entries.jelly	(revision 29921)
+++ src/main/resources/hudson/plugins/ec2/AmazonEC2Cloud/config-entries.jelly	(working copy)
@@ -1,21 +0,0 @@
-<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
-  <f:entry title="${%Region}" field="region">
-    <f:enum>${it.displayName}</f:enum>
-  </f:entry>
-  <f:entry title="${%Access Key ID}" field="accessId">
-    <f:textbox />
-  </f:entry>
-  <f:entry title="${%Secret Access Key}" field="secretKey">
-    <f:password />
-  </f:entry>
-  <f:entry title="${%EC2 RSA Private Key}" field="privateKey">
-    <f:textarea />
-  </f:entry>
-  <f:advanced>
-    <f:entry title="${%Instance Cap}" field="instanceCapStr">
-      <f:textbox />
-    </f:entry>
-  </f:advanced>
-  <f:validateButton title="${%Generate Key}" progress="${%Generate...}" method="generateKey" with="secretKey,accessId" />
-  <f:validateButton title="${%Test Connection}" progress="${%Testing...}" method="testConnection" with="region,secretKey,accessId,privateKey" />
-</j:jelly>
Index: src/main/resources/hudson/plugins/ec2/AmazonEC2Cloud/help-region.html
===================================================================
--- src/main/resources/hudson/plugins/ec2/AmazonEC2Cloud/help-region.html	(revision 29721)
+++ src/main/resources/hudson/plugins/ec2/AmazonEC2Cloud/help-region.html	(working copy)
@@ -1,6 +0,0 @@
-<div>
-    Specifies the geographic region in which your slaves will run. Pick the region closest to you. 
-    Regions can be thought of as
-    independent instances of EC2/S3. See <a href="http://support.rightscale.com/index.php?title=2._References/02-Cloud_Infrastructures/01-Amazon_Web_Services_(AWS)/02-Amazon_EC2/EC2_Regions:__EC2-US_%26_EC2-EU">online resources</a>
-    for more about what regions are.
-</div>
\ No newline at end of file
Index: src/test/java/hudson/plugins/ec2/AmazonEC2CloudTest.java
===================================================================
--- src/test/java/hudson/plugins/ec2/AmazonEC2CloudTest.java	(revision 29721)
+++ src/test/java/hudson/plugins/ec2/AmazonEC2CloudTest.java	(working copy)
@@ -9,7 +9,7 @@
  */
 public class AmazonEC2CloudTest extends HudsonTestCase {
     public void testConfigRoundtrip() throws Exception {
-        AmazonEC2Cloud orig = new AmazonEC2Cloud(AwsRegion.US_EAST_1, "abc", "def", "ghi", "3", Collections.<SlaveTemplate>emptyList());
+        EC2Cloud orig = new EC2Cloud(AwsRegion.US_EAST_1, "abc", "def", "ghi", "3", Collections.<SlaveTemplate>emptyList());
         hudson.clouds.add(orig);
         submit(createWebClient().goTo("configure").getFormByName("config"));