com.cloud.bridge.service.EC2RestServlet.java Source code

Java tutorial

Introduction

Here is the source code for com.cloud.bridge.service.EC2RestServlet.java

Source

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.bridge.service;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.URLEncoder;
import java.security.KeyStore;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.UUID;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMFactory;
import org.apache.axis2.AxisFault;
import org.apache.axis2.databinding.ADBBean;
import org.apache.axis2.databinding.ADBException;
import org.apache.axis2.databinding.utils.writer.MTOMAwareXMLSerializer;
import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;

import com.amazon.ec2.AllocateAddressResponse;
import com.amazon.ec2.AssociateAddressResponse;
import com.amazon.ec2.AttachVolumeResponse;
import com.amazon.ec2.AuthorizeSecurityGroupIngressResponse;
import com.amazon.ec2.CreateImageResponse;
import com.amazon.ec2.CreateKeyPairResponse;
import com.amazon.ec2.CreateSecurityGroupResponse;
import com.amazon.ec2.CreateSnapshotResponse;
import com.amazon.ec2.CreateVolumeResponse;
import com.amazon.ec2.DeleteKeyPairResponse;
import com.amazon.ec2.DeleteSecurityGroupResponse;
import com.amazon.ec2.DeleteSnapshotResponse;
import com.amazon.ec2.DeleteVolumeResponse;
import com.amazon.ec2.DeregisterImageResponse;
import com.amazon.ec2.DescribeAvailabilityZonesResponse;
import com.amazon.ec2.DescribeImageAttributeResponse;
import com.amazon.ec2.DescribeImagesResponse;
import com.amazon.ec2.DescribeInstanceAttributeResponse;
import com.amazon.ec2.DescribeInstancesResponse;
import com.amazon.ec2.DescribeKeyPairsResponse;
import com.amazon.ec2.DescribeSecurityGroupsResponse;
import com.amazon.ec2.DescribeSnapshotsResponse;
import com.amazon.ec2.DescribeVolumesResponse;
import com.amazon.ec2.DetachVolumeResponse;
import com.amazon.ec2.DisassociateAddressResponse;
import com.amazon.ec2.GetPasswordDataResponse;
import com.amazon.ec2.ImportKeyPairResponse;
import com.amazon.ec2.ModifyImageAttributeResponse;
import com.amazon.ec2.RebootInstancesResponse;
import com.amazon.ec2.RegisterImageResponse;
import com.amazon.ec2.ReleaseAddressResponse;
import com.amazon.ec2.ResetImageAttributeResponse;
import com.amazon.ec2.RevokeSecurityGroupIngressResponse;
import com.amazon.ec2.RunInstancesResponse;
import com.amazon.ec2.StartInstancesResponse;
import com.amazon.ec2.StopInstancesResponse;
import com.amazon.ec2.TerminateInstancesResponse;
import com.cloud.bridge.model.UserCredentialsVO;
import com.cloud.bridge.persist.dao.OfferingDaoImpl;
import com.cloud.bridge.persist.dao.UserCredentialsDaoImpl;
import com.cloud.bridge.service.controller.s3.ServiceProvider;
import com.cloud.bridge.service.core.ec2.EC2AssociateAddress;
import com.cloud.bridge.service.core.ec2.EC2AuthorizeRevokeSecurityGroup;
import com.cloud.bridge.service.core.ec2.EC2CreateImage;
import com.cloud.bridge.service.core.ec2.EC2CreateKeyPair;
import com.cloud.bridge.service.core.ec2.EC2CreateVolume;
import com.cloud.bridge.service.core.ec2.EC2DeleteKeyPair;
import com.cloud.bridge.service.core.ec2.EC2DescribeAddresses;
import com.cloud.bridge.service.core.ec2.EC2DescribeAvailabilityZones;
import com.cloud.bridge.service.core.ec2.EC2DescribeImages;
import com.cloud.bridge.service.core.ec2.EC2DescribeInstances;
import com.cloud.bridge.service.core.ec2.EC2DescribeKeyPairs;
import com.cloud.bridge.service.core.ec2.EC2DescribeSecurityGroups;
import com.cloud.bridge.service.core.ec2.EC2DescribeSnapshots;
import com.cloud.bridge.service.core.ec2.EC2DescribeVolumes;
import com.cloud.bridge.service.core.ec2.EC2DisassociateAddress;
import com.cloud.bridge.service.core.ec2.EC2Engine;
import com.cloud.bridge.service.core.ec2.EC2Filter;
import com.cloud.bridge.service.core.ec2.EC2GroupFilterSet;
import com.cloud.bridge.service.core.ec2.EC2Image;
import com.cloud.bridge.service.core.ec2.EC2ImportKeyPair;
import com.cloud.bridge.service.core.ec2.EC2InstanceFilterSet;
import com.cloud.bridge.service.core.ec2.EC2IpPermission;
import com.cloud.bridge.service.core.ec2.EC2KeyPairFilterSet;
import com.cloud.bridge.service.core.ec2.EC2RebootInstances;
import com.cloud.bridge.service.core.ec2.EC2RegisterImage;
import com.cloud.bridge.service.core.ec2.EC2ReleaseAddress;
import com.cloud.bridge.service.core.ec2.EC2RunInstances;
import com.cloud.bridge.service.core.ec2.EC2SecurityGroup;
import com.cloud.bridge.service.core.ec2.EC2SnapshotFilterSet;
import com.cloud.bridge.service.core.ec2.EC2StartInstances;
import com.cloud.bridge.service.core.ec2.EC2StopInstances;
import com.cloud.bridge.service.core.ec2.EC2Volume;
import com.cloud.bridge.service.core.ec2.EC2VolumeFilterSet;
import com.cloud.bridge.service.exception.EC2ServiceException;
import com.cloud.bridge.service.exception.NoSuchObjectException;
import com.cloud.bridge.service.exception.PermissionDeniedException;
import com.cloud.bridge.service.exception.EC2ServiceException.ClientError;
import com.cloud.bridge.util.AuthenticationUtils;
import com.cloud.bridge.util.ConfigurationHelper;
import com.cloud.bridge.util.EC2RestAuth;
import com.cloud.stack.models.CloudStackAccount;
import com.cloud.utils.component.ComponentLocator;
import com.cloud.utils.db.Transaction;

public class EC2RestServlet extends HttpServlet {

    private static final long serialVersionUID = -6168996266762804888L;
    protected final UserCredentialsDaoImpl ucDao = ComponentLocator.inject(UserCredentialsDaoImpl.class);
    protected final OfferingDaoImpl ofDao = ComponentLocator.inject(OfferingDaoImpl.class);

    public static final Logger logger = Logger.getLogger(EC2RestServlet.class);

    private OMFactory factory = OMAbstractFactory.getOMFactory();
    private XMLOutputFactory xmlOutFactory = XMLOutputFactory.newInstance();

    private String pathToKeystore = null;
    private String keystorePassword = null;
    private String wsdlVersion = null;
    private String version = null;

    boolean debug = true;

    /**
     * We build the path to where the keystore holding the WS-Security X509 certificates
     * are stored.
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        File propertiesFile = ConfigurationHelper.findConfigurationFile("ec2-service.properties");
        Properties EC2Prop = null;

        if (null != propertiesFile) {
            logger.info("Use EC2 properties file: " + propertiesFile.getAbsolutePath());
            EC2Prop = new Properties();
            try {
                EC2Prop.load(new FileInputStream(propertiesFile));
            } catch (FileNotFoundException e) {
                logger.warn("Unable to open properties file: " + propertiesFile.getAbsolutePath(), e);
            } catch (IOException e) {
                logger.warn("Unable to read properties file: " + propertiesFile.getAbsolutePath(), e);
            }
            String keystore = EC2Prop.getProperty("keystore");
            keystorePassword = EC2Prop.getProperty("keystorePass");
            wsdlVersion = EC2Prop.getProperty("WSDLVersion", "2010-11-15");
            version = EC2Prop.getProperty("cloudbridgeVersion", "UNKNOWN VERSION");

            String installedPath = System.getenv("CATALINA_HOME");
            if (installedPath == null)
                installedPath = System.getenv("CATALINA_BASE");
            if (installedPath == null)
                installedPath = System.getProperty("catalina.home");
            String webappPath = config.getServletContext().getRealPath("/");
            //pathToKeystore = new String( installedPath + File.separator + "webapps" + File.separator + webappName + File.separator + "WEB-INF" + File.separator + "classes" + File.separator + keystore );
            pathToKeystore = new String(
                    webappPath + "WEB-INF" + File.separator + "classes" + File.separator + keystore);
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        doGetOrPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        doGetOrPost(req, resp);
    }

    protected void doGetOrPost(HttpServletRequest request, HttpServletResponse response) {

        if (debug) {
            System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.request_uri: "
                    + request.getAttribute("javax.servlet.forward.request_uri"));
            System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.context_path: "
                    + request.getAttribute("javax.servlet.forward.context_path"));
            System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.servlet_path: "
                    + request.getAttribute("javax.servlet.forward.servlet_path"));
            System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.path_info: "
                    + request.getAttribute("javax.servlet.forward.path_info"));
            System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.query_string: "
                    + request.getAttribute("javax.servlet.forward.query_string"));

        }

        String action = request.getParameter("Action");
        logRequest(request);

        // -> unauthenticated calls, should still be done over HTTPS
        if (action.equalsIgnoreCase("SetUserKeys")) {
            setUserKeys(request, response);
            return;
        }

        if (action.equalsIgnoreCase("CloudEC2Version")) {
            cloudEC2Version(request, response);
            return;
        }

        // -> authenticated calls
        try {
            if (!authenticateRequest(request, response))
                return;

            if (action.equalsIgnoreCase("AllocateAddress"))
                allocateAddress(request, response);
            else if (action.equalsIgnoreCase("AssociateAddress"))
                associateAddress(request, response);
            else if (action.equalsIgnoreCase("AttachVolume"))
                attachVolume(request, response);
            else if (action.equalsIgnoreCase("AuthorizeSecurityGroupIngress"))
                authorizeSecurityGroupIngress(request, response);
            else if (action.equalsIgnoreCase("CreateImage"))
                createImage(request, response);
            else if (action.equalsIgnoreCase("CreateSecurityGroup"))
                createSecurityGroup(request, response);
            else if (action.equalsIgnoreCase("CreateSnapshot"))
                createSnapshot(request, response);
            else if (action.equalsIgnoreCase("CreateVolume"))
                createVolume(request, response);
            else if (action.equalsIgnoreCase("DeleteSecurityGroup"))
                deleteSecurityGroup(request, response);
            else if (action.equalsIgnoreCase("DeleteSnapshot"))
                deleteSnapshot(request, response);
            else if (action.equalsIgnoreCase("DeleteVolume"))
                deleteVolume(request, response);
            else if (action.equalsIgnoreCase("DeregisterImage"))
                deregisterImage(request, response);
            else if (action.equalsIgnoreCase("DescribeAddresses"))
                describeAddresses(request, response);
            else if (action.equalsIgnoreCase("DescribeAvailabilityZones"))
                describeAvailabilityZones(request, response);
            else if (action.equalsIgnoreCase("DescribeImageAttribute"))
                describeImageAttribute(request, response);
            else if (action.equalsIgnoreCase("DescribeImages"))
                describeImages(request, response);
            else if (action.equalsIgnoreCase("DescribeInstanceAttribute"))
                describeInstanceAttribute(request, response);
            else if (action.equalsIgnoreCase("DescribeInstances"))
                describeInstances(request, response);
            else if (action.equalsIgnoreCase("DescribeSecurityGroups"))
                describeSecurityGroups(request, response);
            else if (action.equalsIgnoreCase("DescribeSnapshots"))
                describeSnapshots(request, response);
            else if (action.equalsIgnoreCase("DescribeVolumes"))
                describeVolumes(request, response);
            else if (action.equalsIgnoreCase("DetachVolume"))
                detachVolume(request, response);
            else if (action.equalsIgnoreCase("DisassociateAddress"))
                disassociateAddress(request, response);
            else if (action.equalsIgnoreCase("ModifyImageAttribute"))
                modifyImageAttribute(request, response);
            else if (action.equalsIgnoreCase("RebootInstances"))
                rebootInstances(request, response);
            else if (action.equalsIgnoreCase("RegisterImage"))
                registerImage(request, response);
            else if (action.equalsIgnoreCase("ReleaseAddress"))
                releaseAddress(request, response);
            else if (action.equalsIgnoreCase("ResetImageAttribute"))
                resetImageAttribute(request, response);
            else if (action.equalsIgnoreCase("RevokeSecurityGroupIngress"))
                revokeSecurityGroupIngress(request, response);
            else if (action.equalsIgnoreCase("RunInstances"))
                runInstances(request, response);
            else if (action.equalsIgnoreCase("StartInstances"))
                startInstances(request, response);
            else if (action.equalsIgnoreCase("StopInstances"))
                stopInstances(request, response);
            else if (action.equalsIgnoreCase("TerminateInstances"))
                terminateInstances(request, response);
            else if (action.equalsIgnoreCase("SetCertificate"))
                setCertificate(request, response);
            else if (action.equalsIgnoreCase("DeleteCertificate"))
                deleteCertificate(request, response);
            else if (action.equalsIgnoreCase("SetOfferMapping"))
                setOfferMapping(request, response);
            else if (action.equalsIgnoreCase("DeleteOfferMapping"))
                deleteOfferMapping(request, response);
            else if (action.equalsIgnoreCase("CreateKeyPair"))
                createKeyPair(request, response);
            else if (action.equalsIgnoreCase("ImportKeyPair"))
                importKeyPair(request, response);
            else if (action.equalsIgnoreCase("DeleteKeyPair"))
                deleteKeyPair(request, response);
            else if (action.equalsIgnoreCase("DescribeKeyPairs"))
                describeKeyPairs(request, response);
            else if (action.equalsIgnoreCase("GetPasswordData"))
                getPasswordData(request, response);
            else {
                logger.error("Unsupported action " + action);
                throw new EC2ServiceException(ClientError.Unsupported, "This operation is not available");
            }

        } catch (EC2ServiceException e) {
            response.setStatus(e.getErrorCode());

            if (e.getCause() != null && e.getCause() instanceof AxisFault)
                faultResponse(response, ((AxisFault) e.getCause()).getFaultCode().getLocalPart(), e.getMessage());
            else {
                logger.error("EC2ServiceException: " + e.getMessage(), e);
                endResponse(response, e.toString());
            }
        } catch (PermissionDeniedException e) {
            logger.error("Unexpected exception: " + e.getMessage(), e);
            response.setStatus(403);
            endResponse(response, "Access denied");

        } catch (Exception e) {
            logger.error("Unexpected exception: " + e.getMessage(), e);
            response.setStatus(500);
            endResponse(response, e.toString());

        } finally {
            try {
                response.flushBuffer();
            } catch (IOException e) {
                logger.error("Unexpected exception " + e.getMessage(), e);
            }
        }
    }

    /**
     * Provide an easy way to determine the version of the implementation running.
     * 
     * This is an unauthenticated REST call.
     */
    private void cloudEC2Version(HttpServletRequest request, HttpServletResponse response) {
        String version_response = new String(
                "<?xml version=\"1.0\" encoding=\"utf-8\"?><CloudEC2Version>" + version + "</CloudEC2Version>");
        response.setStatus(200);
        endResponse(response, version_response);
    }

    /**
     * This request registers the Cloud.com account holder to the EC2 service.   The Cloud.com
     * account holder saves his API access and secret keys with the EC2 service so that 
     * the EC2 service can make Cloud.com API calls on his behalf.   The given API access
     * and secret key are saved into the "usercredentials" database table.   
     * 
     * This is an unauthenticated REST call.   The only required parameters are 'accesskey' and
     * 'secretkey'. 
     * 
     * To verify that the given keys represent an existing account they are used to execute the
     * Cloud.com's listAccounts API function.   If the keys do not represent a valid account the
     * listAccounts function will fail.
     * 
     * A user can call this REST function any number of times, on each call the Cloud.com secret
     * key is simply over writes any previously stored value.
     * 
     * As with all REST calls HTTPS should be used to ensure their security.
     */
    private void setUserKeys(HttpServletRequest request, HttpServletResponse response) {
        String[] accessKey = null;
        String[] secretKey = null;
        Transaction txn = null;
        try {
            // -> all these parameters are required
            accessKey = request.getParameterValues("accesskey");
            if (null == accessKey || 0 == accessKey.length) {
                response.sendError(530, "Missing accesskey parameter");
                return;
            }

            secretKey = request.getParameterValues("secretkey");
            if (null == secretKey || 0 == secretKey.length) {
                response.sendError(530, "Missing secretkey parameter");
                return;
            }
        } catch (Exception e) {
            logger.error("SetUserKeys exception " + e.getMessage(), e);
            response.setStatus(500);
            endResponse(response, "SetUserKeys exception " + e.getMessage());
            return;
        }

        // prime UserContext here
        //       logger.debug("initializing context");
        UserContext context = UserContext.current();

        try {
            txn = Transaction.open(Transaction.AWSAPI_DB);
            // -> use the keys to see if the account actually exists
            ServiceProvider.getInstance().getEC2Engine().validateAccount(accessKey[0], secretKey[0]);
            /*           UserCredentialsDao credentialDao = new UserCredentialsDao();
                       credentialDao.setUserKeys(  ); 
            */ UserCredentialsVO user = new UserCredentialsVO(accessKey[0], secretKey[0]);
            ucDao.persist(user);
            txn.commit();

        } catch (Exception e) {
            logger.error("SetUserKeys " + e.getMessage(), e);
            response.setStatus(401);
            endResponse(response, e.toString());
            txn.close();
            return;
        }
        response.setStatus(200);
        endResponse(response, "User keys set successfully");
    }

    /**
     * The SOAP API for EC2 uses WS-Security to sign all client requests.  This requires that 
     * the client have a public/private key pair and the public key defined by a X509 certificate.
     * Thus in order for a Cloud.com account holder to use the EC2's SOAP API he must register
     * his X509 certificate with the EC2 service.   This function allows the Cloud.com account
     * holder to "load" his X509 certificate into the service.   Note, that the SetUserKeys REST
     * function must be called before this call.
     * 
     * This is an authenticated REST call and as such must contain all the required REST parameters
     * including: Signature, Timestamp, Expires, etc.   The signature is calculated using the
     * Cloud.com account holder's API access and secret keys and the Amazon defined EC2 signature
     * algorithm.
     * 
     * A user can call this REST function any number of times, on each call the X509 certificate
     * simply over writes any previously stored value.
     */
    private void setCertificate(HttpServletRequest request, HttpServletResponse response) throws Exception {
        Transaction txn = null;
        try {
            // [A] Pull the cert and cloud AccessKey from the request
            String[] certificate = request.getParameterValues("cert");
            if (null == certificate || 0 == certificate.length) {
                response.sendError(530, "Missing cert parameter");
                return;
            }
            //           logger.debug( "SetCertificate cert: [" + certificate[0] + "]" );

            String[] accessKey = request.getParameterValues("AWSAccessKeyId");
            if (null == accessKey || 0 == accessKey.length) {
                response.sendError(530, "Missing AWSAccessKeyId parameter");
                return;
            }

            // [B] Open our keystore
            FileInputStream fsIn = new FileInputStream(pathToKeystore);
            KeyStore certStore = KeyStore.getInstance("JKS");
            certStore.load(fsIn, keystorePassword.toCharArray());

            // -> use the Cloud API key to save the cert in the keystore
            // -> write the cert into the keystore on disk
            Certificate userCert = null;
            CertificateFactory cf = CertificateFactory.getInstance("X.509");

            ByteArrayInputStream bs = new ByteArrayInputStream(certificate[0].getBytes());
            while (bs.available() > 0)
                userCert = cf.generateCertificate(bs);
            certStore.setCertificateEntry(accessKey[0], userCert);

            FileOutputStream fsOut = new FileOutputStream(pathToKeystore);
            certStore.store(fsOut, keystorePassword.toCharArray());

            // [C] Associate the cert's uniqueId with the Cloud API keys
            String uniqueId = AuthenticationUtils.X509CertUniqueId(userCert);
            logger.debug("SetCertificate, uniqueId: " + uniqueId);
            /*           UserCredentialsDao credentialDao = new UserCredentialsDao();
                       credentialDao.setCertificateId( accessKey[0], uniqueId );
            */
            txn = Transaction.open(Transaction.AWSAPI_DB);
            UserCredentialsVO user = ucDao.getByAccessKey(accessKey[0]);
            user.setCertUniqueId(uniqueId);
            ucDao.update(user.getId(), user);
            response.setStatus(200);
            endResponse(response, "User certificate set successfully");
            txn.commit();

        } catch (NoSuchObjectException e) {
            logger.error("SetCertificate exception " + e.getMessage(), e);
            response.sendError(404, "SetCertificate exception " + e.getMessage());

        } catch (Exception e) {
            logger.error("SetCertificate exception " + e.getMessage(), e);
            response.sendError(500, "SetCertificate exception " + e.getMessage());
        } finally {
            txn.close();
        }

    }

    /**
     * The SOAP API for EC2 uses WS-Security to sign all client requests.  This requires that 
     * the client have a public/private key pair and the public key defined by a X509 certificate.
     * This REST call allows a Cloud.com account holder to remove a previouly "loaded" X509
     * certificate out of the EC2 service.
     * 
     * This is an unauthenticated REST call and as such must contain all the required REST parameters
     * including: Signature, Timestamp, Expires, etc.   The signature is calculated using the
     * Cloud.com account holder's API access and secret keys and the Amazon defined EC2 signature
     * algorithm.
     */
    private void deleteCertificate(HttpServletRequest request, HttpServletResponse response) throws Exception {
        Transaction txn = null;
        try {
            String[] accessKey = request.getParameterValues("AWSAccessKeyId");
            if (null == accessKey || 0 == accessKey.length) {
                response.sendError(530, "Missing AWSAccessKeyId parameter");
                return;
            }

            // -> delete the specified entry and save back to disk
            FileInputStream fsIn = new FileInputStream(pathToKeystore);
            KeyStore certStore = KeyStore.getInstance("JKS");
            certStore.load(fsIn, keystorePassword.toCharArray());

            if (certStore.containsAlias(accessKey[0])) {
                certStore.deleteEntry(accessKey[0]);
                FileOutputStream fsOut = new FileOutputStream(pathToKeystore);
                certStore.store(fsOut, keystorePassword.toCharArray());

                // -> dis-associate the cert's uniqueId with the Cloud API keys
                /*                UserCredentialsDao credentialDao = new UserCredentialsDao();
                                credentialDao.setCertificateId( accessKey[0], null );
                                    
                */ txn = Transaction.open(Transaction.AWSAPI_DB);
                UserCredentialsVO user = ucDao.getByAccessKey(accessKey[0]);
                user.setCertUniqueId(null);
                ucDao.update(user.getId(), user);
                response.setStatus(200);
                endResponse(response, "User certificate deleted successfully");
                txn.commit();
            } else
                response.setStatus(404);

        } catch (NoSuchObjectException e) {
            logger.error("SetCertificate exception " + e.getMessage(), e);
            response.sendError(404, "SetCertificate exception " + e.getMessage());

        } catch (Exception e) {
            logger.error("DeleteCertificate exception " + e.getMessage(), e);
            response.sendError(500, "DeleteCertificate exception " + e.getMessage());
        } finally {
            txn.close();
        }
    }

    /**
     * Allow the caller to define the mapping between the Amazon instance type strings
     * (e.g., m1.small, cc1.4xlarge) and the cloudstack service offering ids.  Setting
     * an existing mapping just over writes the prevous values.
     */
    private void setOfferMapping(HttpServletRequest request, HttpServletResponse response) {
        String amazonOffer = null;
        String cloudOffer = null;

        try {
            // -> all these parameters are required
            amazonOffer = request.getParameter("amazonoffer");
            if (null == amazonOffer) {
                response.sendError(530, "Missing amazonoffer parameter");
                return;
            }

            cloudOffer = request.getParameter("cloudoffer");
            if (null == cloudOffer) {
                response.sendError(530, "Missing cloudoffer parameter");
                return;
            }
        } catch (Exception e) {
            logger.error("SetOfferMapping exception " + e.getMessage(), e);
            response.setStatus(500);
            endResponse(response, "SetOfferMapping exception " + e.getMessage());
            return;
        }

        // validate account is admin level
        try {
            CloudStackAccount currentAccount = ServiceProvider.getInstance().getEC2Engine().getCurrentAccount();

            if (currentAccount.getAccountType() != 1) {
                logger.debug("SetOfferMapping called by non-admin user!");
                response.setStatus(500);
                endResponse(response, "Permission denied for non-admin user to setOfferMapping!");
                return;
            }
        } catch (Exception e) {
            logger.error("SetOfferMapping " + e.getMessage(), e);
            response.setStatus(401);
            endResponse(response, e.toString());
            return;
        }

        try {

            ofDao.setOfferMapping(amazonOffer, cloudOffer);

        } catch (Exception e) {
            logger.error("SetOfferMapping " + e.getMessage(), e);
            response.setStatus(401);
            endResponse(response, e.toString());
            return;
        }
        response.setStatus(200);
        endResponse(response, "offering mapping set successfully");
    }

    private void deleteOfferMapping(HttpServletRequest request, HttpServletResponse response) {
        String amazonOffer = null;

        try {
            // -> all these parameters are required
            amazonOffer = request.getParameter("amazonoffer");
            if (null == amazonOffer) {
                response.sendError(530, "Missing amazonoffer parameter");
                return;
            }

        } catch (Exception e) {
            logger.error("DeleteOfferMapping exception " + e.getMessage(), e);
            response.setStatus(500);
            endResponse(response, "DeleteOfferMapping exception " + e.getMessage());
            return;
        }

        // validate account is admin level
        try {
            CloudStackAccount currentAccount = ServiceProvider.getInstance().getEC2Engine().getCurrentAccount();

            if (currentAccount.getAccountType() != 1) {
                logger.debug("deleteOfferMapping called by non-admin user!");
                response.setStatus(500);
                endResponse(response, "Permission denied for non-admin user to deleteOfferMapping!");
                return;
            }
        } catch (Exception e) {
            logger.error("deleteOfferMapping " + e.getMessage(), e);
            response.setStatus(401);
            endResponse(response, e.toString());
            return;
        }

        try {
            ofDao.deleteOfferMapping(amazonOffer);
        } catch (Exception e) {
            logger.error("DeleteOfferMapping " + e.getMessage(), e);
            response.setStatus(401);
            endResponse(response, e.toString());
            return;
        }
        response.setStatus(200);
        endResponse(response, "offering mapping deleted successfully");
    }

    /**
     * The approach taken here is to map these REST calls into the same objects used 
     * to implement the matching SOAP requests (e.g., AttachVolume).   This is done by parsing
     * out the URL parameters and loading them into the relevant EC2XXX object(s).   Once
     * the parameters are loaded the appropriate EC2Engine function is called to perform
     * the requested action.   The result of the EC2Engine function is a standard 
     * Amazon WSDL defined object (e.g., AttachVolumeResponse Java object).   Finally the
     * serialize method is called on the returned response object to obtain the extected
     * response XML.
     */
    private void attachVolume(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2Volume EC2request = new EC2Volume();

        // -> all these parameters are required
        String[] volumeId = request.getParameterValues("VolumeId");
        if (null != volumeId && 0 < volumeId.length)
            EC2request.setId(volumeId[0]);
        else {
            response.sendError(530, "Missing VolumeId parameter");
            return;
        }

        String[] instanceId = request.getParameterValues("InstanceId");
        if (null != instanceId && 0 < instanceId.length)
            EC2request.setInstanceId(instanceId[0]);
        else {
            response.sendError(530, "Missing InstanceId parameter");
            return;
        }

        String[] device = request.getParameterValues("Device");
        if (null != device && 0 < device.length)
            EC2request.setDevice(device[0]);
        else {
            response.sendError(530, "Missing Device parameter");
            return;
        }

        // -> execute the request
        AttachVolumeResponse EC2response = EC2SoapServiceImpl
                .toAttachVolumeResponse(ServiceProvider.getInstance().getEC2Engine().attachVolume(EC2request));
        serializeResponse(response, EC2response);
    }

    /**
     * The SOAP equivalent of this function appears to allow multiple permissions per request, yet
     * in the REST API documentation only one permission is allowed.
     */
    private void revokeSecurityGroupIngress(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2AuthorizeRevokeSecurityGroup EC2request = new EC2AuthorizeRevokeSecurityGroup();

        String[] groupName = request.getParameterValues("GroupName");
        if (null != groupName && 0 < groupName.length)
            EC2request.setName(groupName[0]);
        else {
            response.sendError(530, "Missing GroupName parameter");
            return;
        }

        EC2IpPermission perm = new EC2IpPermission();

        String[] protocol = request.getParameterValues("IpProtocol");
        if (null != protocol && 0 < protocol.length)
            perm.setProtocol(protocol[0]);
        else {
            response.sendError(530, "Missing IpProtocol parameter");
            return;
        }

        String[] fromPort = request.getParameterValues("FromPort");
        if (null != fromPort && 0 < fromPort.length)
            perm.setProtocol(fromPort[0]);
        else {
            response.sendError(530, "Missing FromPort parameter");
            return;
        }

        String[] toPort = request.getParameterValues("ToPort");
        if (null != toPort && 0 < toPort.length)
            perm.setProtocol(toPort[0]);
        else {
            response.sendError(530, "Missing ToPort parameter");
            return;
        }

        String[] ranges = request.getParameterValues("CidrIp");
        if (null != ranges && 0 < ranges.length)
            perm.addIpRange(ranges[0]);
        else {
            response.sendError(530, "Missing CidrIp parameter");
            return;
        }

        String[] user = request.getParameterValues("SourceSecurityGroupOwnerId");
        if (null == user || 0 == user.length) {
            response.sendError(530, "Missing SourceSecurityGroupOwnerId parameter");
            return;
        }

        String[] name = request.getParameterValues("SourceSecurityGroupName");
        if (null == name || 0 == name.length) {
            response.sendError(530, "Missing SourceSecurityGroupName parameter");
            return;
        }

        EC2SecurityGroup group = new EC2SecurityGroup();
        group.setAccount(user[0]);
        group.setName(name[0]);
        perm.addUser(group);
        EC2request.addIpPermission(perm);

        // -> execute the request
        RevokeSecurityGroupIngressResponse EC2response = EC2SoapServiceImpl.toRevokeSecurityGroupIngressResponse(
                ServiceProvider.getInstance().getEC2Engine().revokeSecurityGroup(EC2request));
        serializeResponse(response, EC2response);
    }

    private void authorizeSecurityGroupIngress(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        // -> parse the complicated paramters into our standard object
        EC2AuthorizeRevokeSecurityGroup EC2request = new EC2AuthorizeRevokeSecurityGroup();

        String[] groupName = request.getParameterValues("GroupName");
        if (null != groupName && 0 < groupName.length)
            EC2request.setName(groupName[0]);
        else {
            response.sendError(530, "Missing GroupName parameter");
            return;
        }

        // -> not clear how many parameters there are until we fail to get IpPermissions.n.IpProtocol
        int nCount = 1;
        do {
            EC2IpPermission perm = new EC2IpPermission();

            String[] protocol = request.getParameterValues("IpPermissions." + nCount + ".IpProtocol");
            if (null != protocol && 0 < protocol.length)
                perm.setProtocol(protocol[0]);
            else
                break;

            String[] fromPort = request.getParameterValues("IpPermissions." + nCount + ".FromPort");
            if (null != fromPort && 0 < fromPort.length)
                perm.setProtocol(fromPort[0]);

            String[] toPort = request.getParameterValues("IpPermissions." + nCount + ".ToPort");
            if (null != toPort && 0 < toPort.length)
                perm.setProtocol(toPort[0]);

            // -> list: IpPermissions.n.IpRanges.m.CidrIp
            int mCount = 1;
            do {
                String[] ranges = request
                        .getParameterValues("IpPermissions." + nCount + ".IpRanges." + mCount + ".CidrIp");
                if (null != ranges && 0 < ranges.length)
                    perm.addIpRange(ranges[0]);
                else
                    break;
                mCount++;

            } while (true);

            // -> list: IpPermissions.n.Groups.m.UserId and IpPermissions.n.Groups.m.GroupName 
            mCount = 1;
            do {
                String[] user = request
                        .getParameterValues("IpPermissions." + nCount + ".Groups." + mCount + ".UserId");
                if (null == user || 0 == user.length)
                    break;

                String[] name = request
                        .getParameterValues("IpPermissions." + nCount + ".Groups." + mCount + ".GroupName");
                if (null == name || 0 == name.length)
                    break;

                EC2SecurityGroup group = new EC2SecurityGroup();
                group.setAccount(user[0]);
                group.setName(name[0]);
                perm.addUser(group);
                mCount++;

            } while (true);

            // -> multiple IP permissions can be specified per group name
            EC2request.addIpPermission(perm);
            nCount++;

        } while (true);

        if (1 == nCount) {
            response.sendError(530, "At least one IpPermissions required");
            return;
        }

        // -> execute the request
        AuthorizeSecurityGroupIngressResponse EC2response = EC2SoapServiceImpl
                .toAuthorizeSecurityGroupIngressResponse(
                        ServiceProvider.getInstance().getEC2Engine().authorizeSecurityGroup(EC2request));
        serializeResponse(response, EC2response);
    }

    private void detachVolume(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2Volume EC2request = new EC2Volume();

        String[] volumeId = request.getParameterValues("VolumeId");
        if (null != volumeId && 0 < volumeId.length)
            EC2request.setId(volumeId[0]);
        else {
            response.sendError(530, "Missing VolumeId parameter");
            return;
        }

        String[] instanceId = request.getParameterValues("InstanceId");
        if (null != instanceId && 0 < instanceId.length)
            EC2request.setInstanceId(instanceId[0]);

        String[] device = request.getParameterValues("Device");
        if (null != device && 0 < device.length)
            EC2request.setDevice(device[0]);

        // -> execute the request
        DetachVolumeResponse EC2response = EC2SoapServiceImpl
                .toDetachVolumeResponse(ServiceProvider.getInstance().getEC2Engine().detachVolume(EC2request));
        serializeResponse(response, EC2response);
    }

    private void deleteVolume(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2Volume EC2request = new EC2Volume();

        String[] volumeId = request.getParameterValues("VolumeId");
        if (null != volumeId && 0 < volumeId.length)
            EC2request.setId(volumeId[0]);
        else {
            response.sendError(530, "Missing VolumeId parameter");
            return;
        }

        // -> execute the request
        DeleteVolumeResponse EC2response = EC2SoapServiceImpl
                .toDeleteVolumeResponse(ServiceProvider.getInstance().getEC2Engine().deleteVolume(EC2request));
        serializeResponse(response, EC2response);
    }

    private void createVolume(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2CreateVolume EC2request = new EC2CreateVolume();

        String[] zoneName = request.getParameterValues("AvailabilityZone");
        if (null != zoneName && 0 < zoneName.length)
            EC2request.setZoneName(zoneName[0]);
        else {
            response.sendError(530, "Missing AvailabilityZone parameter");
            return;
        }

        String[] size = request.getParameterValues("Size");
        String[] snapshotId = request.getParameterValues("SnapshotId");
        boolean useSnapshot = false;
        boolean useSize = false;

        if (null != size && 0 < size.length)
            useSize = true;

        if (snapshotId != null && snapshotId.length != 0)
            useSnapshot = true;

        if (useSize && !useSnapshot) {
            EC2request.setSize(size[0]);
        } else if (useSnapshot && !useSize) {
            EC2request.setSnapshotId(snapshotId[0]);
        } else if (useSize && useSnapshot) {
            response.sendError(530, "Size and SnapshotId parameters are mutually exclusive");
            return;
        } else {
            response.sendError(530, "Size or SnapshotId has to be specified");
            return;
        }

        // -> execute the request
        CreateVolumeResponse EC2response = EC2SoapServiceImpl
                .toCreateVolumeResponse(ServiceProvider.getInstance().getEC2Engine().createVolume(EC2request));
        serializeResponse(response, EC2response);
    }

    private void createSecurityGroup(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {

        String groupName, groupDescription = null;

        String[] name = request.getParameterValues("GroupName");
        if (null != name && 0 < name.length)
            groupName = name[0];
        else {
            response.sendError(530, "Missing GroupName parameter");
            return;
        }

        String[] desc = request.getParameterValues("GroupDescription");
        if (null != desc && 0 < desc.length)
            groupDescription = desc[0];
        else {
            response.sendError(530, "Missing GroupDescription parameter");
            return;
        }

        // -> execute the request
        CreateSecurityGroupResponse EC2response = EC2SoapServiceImpl.toCreateSecurityGroupResponse(
                ServiceProvider.getInstance().getEC2Engine().createSecurityGroup(groupName, groupDescription));
        serializeResponse(response, EC2response);
    }

    private void deleteSecurityGroup(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        String groupName = null;

        String[] name = request.getParameterValues("GroupName");
        if (null != name && 0 < name.length)
            groupName = name[0];
        else {
            response.sendError(530, "Missing GroupName parameter");
            return;
        }

        // -> execute the request
        DeleteSecurityGroupResponse EC2response = EC2SoapServiceImpl.toDeleteSecurityGroupResponse(
                ServiceProvider.getInstance().getEC2Engine().deleteSecurityGroup(groupName));
        serializeResponse(response, EC2response);
    }

    private void deleteSnapshot(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        String snapshotId = null;

        String[] snapSet = request.getParameterValues("SnapshotId");
        if (null != snapSet && 0 < snapSet.length)
            snapshotId = snapSet[0];
        else {
            response.sendError(530, "Missing SnapshotId parameter");
            return;
        }

        // -> execute the request
        DeleteSnapshotResponse EC2response = EC2SoapServiceImpl
                .toDeleteSnapshotResponse(ServiceProvider.getInstance().getEC2Engine().deleteSnapshot(snapshotId));
        serializeResponse(response, EC2response);
    }

    private void createSnapshot(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        String volumeId = null;

        String[] volSet = request.getParameterValues("VolumeId");
        if (null != volSet && 0 < volSet.length)
            volumeId = volSet[0];
        else {
            response.sendError(530, "Missing VolumeId parameter");
            return;
        }

        // -> execute the request
        EC2Engine engine = ServiceProvider.getInstance().getEC2Engine();
        CreateSnapshotResponse EC2response = EC2SoapServiceImpl
                .toCreateSnapshotResponse(engine.createSnapshot(volumeId), engine);
        serializeResponse(response, EC2response);
    }

    private void deregisterImage(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2Image image = new EC2Image();

        String[] imageId = request.getParameterValues("ImageId");
        if (null != imageId && 0 < imageId.length)
            image.setId(imageId[0]);
        else {
            response.sendError(530, "Missing ImageId parameter");
            return;
        }

        // -> execute the request
        DeregisterImageResponse EC2response = EC2SoapServiceImpl
                .toDeregisterImageResponse(ServiceProvider.getInstance().getEC2Engine().deregisterImage(image));
        serializeResponse(response, EC2response);
    }

    private void createImage(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2CreateImage EC2request = new EC2CreateImage();

        String[] instanceId = request.getParameterValues("InstanceId");
        if (null != instanceId && 0 < instanceId.length)
            EC2request.setInstanceId(instanceId[0]);
        else {
            response.sendError(530, "Missing InstanceId parameter");
            return;
        }

        String[] name = request.getParameterValues("Name");
        if (null != name && 0 < name.length)
            EC2request.setName(name[0]);
        else {
            response.sendError(530, "Missing Name parameter");
            return;
        }

        String[] description = request.getParameterValues("Description");
        if (null != description && 0 < description.length)
            EC2request.setDescription(description[0]);

        // -> execute the request
        CreateImageResponse EC2response = EC2SoapServiceImpl
                .toCreateImageResponse(ServiceProvider.getInstance().getEC2Engine().createImage(EC2request));
        serializeResponse(response, EC2response);
    }

    private void registerImage(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2RegisterImage EC2request = new EC2RegisterImage();

        String[] location = request.getParameterValues("ImageLocation");
        if (null != location && 0 < location.length)
            EC2request.setLocation(location[0]);
        else {
            response.sendError(530, "Missing ImageLocation parameter");
            return;
        }

        String[] cloudRedfined = request.getParameterValues("Architecture");
        if (null != cloudRedfined && 0 < cloudRedfined.length)
            EC2request.setArchitecture(cloudRedfined[0]);
        else {
            response.sendError(530, "Missing Architecture parameter");
            return;
        }

        String[] name = request.getParameterValues("Name");
        if (null != name && 0 < name.length)
            EC2request.setName(name[0]);

        String[] description = request.getParameterValues("Description");
        if (null != description && 0 < description.length)
            EC2request.setDescription(description[0]);

        // -> execute the request
        RegisterImageResponse EC2response = EC2SoapServiceImpl
                .toRegisterImageResponse(ServiceProvider.getInstance().getEC2Engine().registerImage(EC2request));
        serializeResponse(response, EC2response);
    }

    private void modifyImageAttribute(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2Image image = new EC2Image();

        // -> its interesting to note that the SOAP API docs has description but the REST API docs do not
        String[] imageId = request.getParameterValues("ImageId");
        if (null != imageId && 0 < imageId.length)
            image.setId(imageId[0]);
        else {
            response.sendError(530, "Missing ImageId parameter");
            return;
        }

        String[] description = request.getParameterValues("Description");
        if (null != description && 0 < description.length)
            image.setDescription(description[0]);
        else {
            response.sendError(530, "Missing Description parameter");
            return;
        }

        // -> execute the request
        ModifyImageAttributeResponse EC2response = EC2SoapServiceImpl.toModifyImageAttributeResponse(
                ServiceProvider.getInstance().getEC2Engine().modifyImageAttribute(image));
        serializeResponse(response, EC2response);
    }

    private void resetImageAttribute(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2Image image = new EC2Image();

        String[] imageId = request.getParameterValues("ImageId");
        if (null != imageId && 0 < imageId.length)
            image.setId(imageId[0]);
        else {
            response.sendError(530, "Missing ImageId parameter");
            return;
        }

        // -> execute the request
        image.setDescription("");
        ResetImageAttributeResponse EC2response = EC2SoapServiceImpl.toResetImageAttributeResponse(
                ServiceProvider.getInstance().getEC2Engine().modifyImageAttribute(image));
        serializeResponse(response, EC2response);
    }

    private void runInstances(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2RunInstances EC2request = new EC2RunInstances();

        // -> so in the Amazon docs for this REST call there is no userData even though there is in the SOAP docs
        String[] imageId = request.getParameterValues("ImageId");
        if (null != imageId && 0 < imageId.length)
            EC2request.setTemplateId(imageId[0]);
        else {
            response.sendError(530, "Missing ImageId parameter");
            return;
        }

        String[] minCount = request.getParameterValues("MinCount");
        if (null != minCount && 0 < minCount.length)
            EC2request.setMinCount(Integer.parseInt(minCount[0]));
        else {
            response.sendError(530, "Missing MinCount parameter");
            return;
        }

        String[] maxCount = request.getParameterValues("MaxCount");
        if (null != maxCount && 0 < maxCount.length)
            EC2request.setMaxCount(Integer.parseInt(maxCount[0]));
        else {
            response.sendError(530, "Missing MaxCount parameter");
            return;
        }

        String[] instanceType = request.getParameterValues("InstanceType");
        if (null != instanceType && 0 < instanceType.length)
            EC2request.setInstanceType(instanceType[0]);

        String[] zoneName = request.getParameterValues("Placement.AvailabilityZone");
        if (null != zoneName && 0 < zoneName.length)
            EC2request.setZoneName(zoneName[0]);

        String[] size = request.getParameterValues("size");
        if (size != null) {
            EC2request.setSize(Integer.valueOf(size[0]));
        }

        String[] keyName = request.getParameterValues("KeyName");
        if (keyName != null) {
            EC2request.setKeyName(keyName[0]);
        }

        // -> execute the request
        EC2Engine engine = ServiceProvider.getInstance().getEC2Engine();
        RunInstancesResponse EC2response = EC2SoapServiceImpl
                .toRunInstancesResponse(engine.runInstances(EC2request), engine);
        serializeResponse(response, EC2response);
    }

    private void rebootInstances(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2RebootInstances EC2request = new EC2RebootInstances();
        int count = 0;

        // -> load in all the "InstanceId.n" parameters if any
        Enumeration<?> names = request.getParameterNames();
        while (names.hasMoreElements()) {
            String key = (String) names.nextElement();
            if (key.startsWith("InstanceId")) {
                String[] value = request.getParameterValues(key);
                if (null != value && 0 < value.length) {
                    EC2request.addInstanceId(value[0]);
                    count++;
                }
            }
        }
        if (0 == count) {
            response.sendError(530, "Missing InstanceId parameter");
            return;
        }

        // -> execute the request
        RebootInstancesResponse EC2response = EC2SoapServiceImpl.toRebootInstancesResponse(
                ServiceProvider.getInstance().getEC2Engine().rebootInstances(EC2request));
        serializeResponse(response, EC2response);
    }

    private void startInstances(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2StartInstances EC2request = new EC2StartInstances();
        int count = 0;

        // -> load in all the "InstanceId.n" parameters if any
        Enumeration<?> names = request.getParameterNames();
        while (names.hasMoreElements()) {
            String key = (String) names.nextElement();
            if (key.startsWith("InstanceId")) {
                String[] value = request.getParameterValues(key);
                if (null != value && 0 < value.length) {
                    EC2request.addInstanceId(value[0]);
                    count++;
                }
            }
        }
        if (0 == count) {
            response.sendError(530, "Missing InstanceId parameter");
            return;
        }

        // -> execute the request
        StartInstancesResponse EC2response = EC2SoapServiceImpl
                .toStartInstancesResponse(ServiceProvider.getInstance().getEC2Engine().startInstances(EC2request));
        serializeResponse(response, EC2response);
    }

    private void stopInstances(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2StopInstances EC2request = new EC2StopInstances();
        int count = 0;

        // -> load in all the "InstanceId.n" parameters if any
        Enumeration<?> names = request.getParameterNames();
        while (names.hasMoreElements()) {
            String key = (String) names.nextElement();
            if (key.startsWith("InstanceId")) {
                String[] value = request.getParameterValues(key);
                if (null != value && 0 < value.length) {
                    EC2request.addInstanceId(value[0]);
                    count++;
                }
            }
        }
        if (0 == count) {
            response.sendError(530, "Missing InstanceId parameter");
            return;
        }

        // -> execute the request
        StopInstancesResponse EC2response = EC2SoapServiceImpl
                .toStopInstancesResponse(ServiceProvider.getInstance().getEC2Engine().stopInstances(EC2request));
        serializeResponse(response, EC2response);
    }

    private void terminateInstances(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2StopInstances EC2request = new EC2StopInstances();
        int count = 0;

        // -> load in all the "InstanceId.n" parameters if any
        Enumeration<?> names = request.getParameterNames();
        while (names.hasMoreElements()) {
            String key = (String) names.nextElement();
            if (key.startsWith("InstanceId")) {
                String[] value = request.getParameterValues(key);
                if (null != value && 0 < value.length) {
                    EC2request.addInstanceId(value[0]);
                    count++;
                }
            }
        }
        if (0 == count) {
            response.sendError(530, "Missing InstanceId parameter");
            return;
        }

        // -> execute the request
        EC2request.setDestroyInstances(true);
        TerminateInstancesResponse EC2response = EC2SoapServiceImpl
                .toTermInstancesResponse(ServiceProvider.getInstance().getEC2Engine().stopInstances(EC2request));
        serializeResponse(response, EC2response);
    }

    /**
     * We are reusing the SOAP code to process this request.   We then use Axiom to serialize the
     * resulting EC2 Amazon object into XML to return to the client.
     */
    private void describeAvailabilityZones(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2DescribeAvailabilityZones EC2request = new EC2DescribeAvailabilityZones();

        // -> load in all the "ZoneName.n" parameters if any
        Enumeration<?> names = request.getParameterNames();
        while (names.hasMoreElements()) {
            String key = (String) names.nextElement();
            if (key.startsWith("ZoneName")) {
                String[] value = request.getParameterValues(key);
                if (null != value && 0 < value.length)
                    EC2request.addZone(value[0]);
            }
        }
        // -> execute the request
        DescribeAvailabilityZonesResponse EC2response = EC2SoapServiceImpl.toDescribeAvailabilityZonesResponse(
                ServiceProvider.getInstance().getEC2Engine().handleRequest(EC2request));
        serializeResponse(response, EC2response);
    }

    private void describeImages(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2DescribeImages EC2request = new EC2DescribeImages();

        // -> load in all the "ImageId.n" parameters if any, and ignore all other parameters
        Enumeration<?> names = request.getParameterNames();
        while (names.hasMoreElements()) {
            String key = (String) names.nextElement();
            if (key.startsWith("ImageId")) {
                String[] value = request.getParameterValues(key);
                if (null != value && 0 < value.length)
                    EC2request.addImageSet(value[0]);
            }
        }
        // -> execute the request
        EC2Engine engine = ServiceProvider.getInstance().getEC2Engine();
        DescribeImagesResponse EC2response = EC2SoapServiceImpl
                .toDescribeImagesResponse(engine.describeImages(EC2request));
        serializeResponse(response, EC2response);
    }

    private void describeImageAttribute(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2DescribeImages EC2request = new EC2DescribeImages();

        // -> only works for queries about descriptions
        String[] descriptions = request.getParameterValues("Description");
        if (null != descriptions && 0 < descriptions.length) {
            String[] value = request.getParameterValues("ImageId");
            EC2request.addImageSet(value[0]);
        } else {
            response.sendError(501, "Unsupported - only description supported");
            return;
        }

        // -> execute the request
        DescribeImageAttributeResponse EC2response = EC2SoapServiceImpl.toDescribeImageAttributeResponse(
                ServiceProvider.getInstance().getEC2Engine().describeImages(EC2request));
        serializeResponse(response, EC2response);
    }

    private void describeInstances(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2DescribeInstances EC2request = new EC2DescribeInstances();

        // -> load in all the "InstanceId.n" parameters if any
        Enumeration<?> names = request.getParameterNames();
        while (names.hasMoreElements()) {
            String key = (String) names.nextElement();
            if (key.startsWith("InstanceId")) {
                String[] value = request.getParameterValues(key);
                if (null != value && 0 < value.length)
                    EC2request.addInstanceId(value[0]);
            }
        }

        // -> are there any filters with this request?
        EC2Filter[] filterSet = extractFilters(request);
        if (null != filterSet) {
            EC2InstanceFilterSet ifs = new EC2InstanceFilterSet();
            for (int i = 0; i < filterSet.length; i++)
                ifs.addFilter(filterSet[i]);
            EC2request.setFilterSet(ifs);
        }

        // -> execute the request
        EC2Engine engine = ServiceProvider.getInstance().getEC2Engine();
        DescribeInstancesResponse EC2response = EC2SoapServiceImpl
                .toDescribeInstancesResponse(engine.describeInstances(EC2request), engine);
        serializeResponse(response, EC2response);
    }

    private void describeAddresses(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2DescribeAddresses ec2Request = new EC2DescribeAddresses();

        // -> load in all the "PublicIp.n" parameters if any
        Enumeration<?> names = request.getParameterNames();
        while (names.hasMoreElements()) {
            String key = (String) names.nextElement();
            if (key.startsWith("PublicIp")) {
                String[] value = request.getParameterValues(key);
                if (null != value && 0 < value.length)
                    ec2Request.addPublicIp(value[0]);
            }
        }
        // -> execute the request
        EC2Engine engine = ServiceProvider.getInstance().getEC2Engine();
        serializeResponse(response,
                EC2SoapServiceImpl.toDescribeAddressesResponse(engine.describeAddresses(ec2Request)));
    }

    private void allocateAddress(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {

        EC2Engine engine = ServiceProvider.getInstance().getEC2Engine();

        AllocateAddressResponse ec2Response = EC2SoapServiceImpl
                .toAllocateAddressResponse(engine.allocateAddress());

        serializeResponse(response, ec2Response);
    }

    private void releaseAddress(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {

        EC2Engine engine = ServiceProvider.getInstance().getEC2Engine();

        String publicIp = request.getParameter("PublicIp");
        if (publicIp == null) {
            response.sendError(530, "Missing PublicIp parameter");
            return;
        }

        EC2ReleaseAddress ec2Request = new EC2ReleaseAddress();
        if (ec2Request != null) {
            ec2Request.setPublicIp(publicIp);
        }

        ReleaseAddressResponse EC2Response = EC2SoapServiceImpl
                .toReleaseAddressResponse(engine.releaseAddress(ec2Request));

        serializeResponse(response, EC2Response);
    }

    private void associateAddress(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2Engine engine = ServiceProvider.getInstance().getEC2Engine();

        String publicIp = request.getParameter("PublicIp");
        if (null == publicIp) {
            response.sendError(530, "Missing PublicIp parameter");
            return;
        }
        String instanceId = request.getParameter("InstanceId");
        if (null == instanceId) {
            response.sendError(530, "Missing InstanceId parameter");
            return;
        }

        EC2AssociateAddress ec2Request = new EC2AssociateAddress();
        if (ec2Request != null) {
            ec2Request.setInstanceId(instanceId);
            ec2Request.setPublicIp(publicIp);
        }

        AssociateAddressResponse ec2Response = EC2SoapServiceImpl
                .toAssociateAddressResponse(engine.associateAddress(ec2Request));

        serializeResponse(response, ec2Response);
    }

    private void disassociateAddress(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2Engine engine = ServiceProvider.getInstance().getEC2Engine();

        String publicIp = request.getParameter("PublicIp");
        if (null == publicIp) {
            response.sendError(530, "Missing PublicIp parameter");
            return;
        }

        EC2DisassociateAddress ec2Request = new EC2DisassociateAddress();
        if (ec2Request != null) {
            ec2Request.setPublicIp(publicIp);
        }

        DisassociateAddressResponse ec2Response = EC2SoapServiceImpl
                .toDisassociateAddressResponse(engine.disassociateAddress(ec2Request));

        serializeResponse(response, ec2Response);
    }

    private void describeSecurityGroups(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2DescribeSecurityGroups EC2request = new EC2DescribeSecurityGroups();

        // -> load in all the "GroupName.n" parameters if any
        Enumeration<?> names = request.getParameterNames();
        while (names.hasMoreElements()) {
            String key = (String) names.nextElement();
            if (key.startsWith("GroupName")) {
                String[] value = request.getParameterValues(key);
                if (null != value && 0 < value.length)
                    EC2request.addGroupName(value[0]);
            }
        }

        // -> are there any filters with this request?
        EC2Filter[] filterSet = extractFilters(request);
        if (null != filterSet) {
            EC2GroupFilterSet gfs = new EC2GroupFilterSet();
            for (EC2Filter filter : filterSet)
                gfs.addFilter(filter);
            EC2request.setFilterSet(gfs);
        }

        // -> execute the request
        EC2Engine engine = ServiceProvider.getInstance().getEC2Engine();

        DescribeSecurityGroupsResponse EC2response = EC2SoapServiceImpl
                .toDescribeSecurityGroupsResponse(engine.describeSecurityGroups(EC2request));
        serializeResponse(response, EC2response);
    }

    private void describeInstanceAttribute(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2DescribeInstances EC2request = new EC2DescribeInstances();
        String instanceType = null;

        // -> we are only handling queries about the "Attribute=instanceType"
        Enumeration<?> names = request.getParameterNames();
        while (names.hasMoreElements()) {
            String key = (String) names.nextElement();
            if (key.startsWith("Attribute")) {
                String[] value = request.getParameterValues(key);
                if (null != value && 0 < value.length && value[0].equalsIgnoreCase("instanceType")) {
                    instanceType = value[0];
                    break;
                }
            }
        }
        if (null != instanceType) {
            String[] value = request.getParameterValues("InstanceId");
            EC2request.addInstanceId(value[0]);
        } else {
            response.sendError(501, "Unsupported - only instanceType supported");
            return;
        }

        // -> execute the request
        DescribeInstanceAttributeResponse EC2response = EC2SoapServiceImpl.toDescribeInstanceAttributeResponse(
                ServiceProvider.getInstance().getEC2Engine().describeInstances(EC2request));
        serializeResponse(response, EC2response);
    }

    private void describeSnapshots(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2DescribeSnapshots EC2request = new EC2DescribeSnapshots();

        // -> load in all the "SnapshotId.n" parameters if any, and ignore any other parameters
        Enumeration<?> names = request.getParameterNames();
        while (names.hasMoreElements()) {
            String key = (String) names.nextElement();
            if (key.startsWith("SnapshotId")) {
                String[] value = request.getParameterValues(key);
                if (null != value && 0 < value.length)
                    EC2request.addSnapshotId(value[0]);
            }
        }

        // -> are there any filters with this request?
        EC2Filter[] filterSet = extractFilters(request);
        if (null != filterSet) {
            EC2SnapshotFilterSet sfs = new EC2SnapshotFilterSet();
            for (int i = 0; i < filterSet.length; i++)
                sfs.addFilter(filterSet[i]);
            EC2request.setFilterSet(sfs);
        }

        // -> execute the request
        EC2Engine engine = ServiceProvider.getInstance().getEC2Engine();
        DescribeSnapshotsResponse EC2response = EC2SoapServiceImpl
                .toDescribeSnapshotsResponse(engine.handleRequest(EC2request));
        serializeResponse(response, EC2response);
    }

    private void describeVolumes(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2DescribeVolumes EC2request = new EC2DescribeVolumes();

        // -> load in all the "VolumeId.n" parameters if any
        Enumeration<?> names = request.getParameterNames();
        while (names.hasMoreElements()) {
            String key = (String) names.nextElement();
            if (key.startsWith("VolumeId")) {
                String[] value = request.getParameterValues(key);
                if (null != value && 0 < value.length)
                    EC2request.addVolumeId(value[0]);
            }
        }

        // -> are there any filters with this request?
        EC2Filter[] filterSet = extractFilters(request);
        if (null != filterSet) {
            EC2VolumeFilterSet vfs = new EC2VolumeFilterSet();
            for (int i = 0; i < filterSet.length; i++)
                vfs.addFilter(filterSet[i]);
            EC2request.setFilterSet(vfs);
        }

        // -> execute the request
        DescribeVolumesResponse EC2response = EC2SoapServiceImpl
                .toDescribeVolumesResponse(ServiceProvider.getInstance().getEC2Engine().handleRequest(EC2request));
        serializeResponse(response, EC2response);
    }

    /**
     * Example of how the filters are defined in a REST request:
     * https://<server>/?Action=DescribeVolumes
     * &Filter.1.Name=attachment.instance-id
     * &Filter.1.Value.1=i-1a2b3c4d
     * &Filter.2.Name=attachment.delete-on-termination
     * &Filter.2.Value.1=true
     * 
     * @param request
     * @return List<EC2Filter>
     */
    private EC2Filter[] extractFilters(HttpServletRequest request) {
        String filterName = null;
        String value = null;
        EC2Filter nextFilter = null;
        boolean timeFilter = false;
        int filterCount = 1;
        int valueCount = 1;

        List<EC2Filter> filterSet = new ArrayList<EC2Filter>();

        do {
            filterName = request.getParameter("Filter." + filterCount + ".Name");
            if (null != filterName) {
                nextFilter = new EC2Filter();
                nextFilter.setName(filterName);
                timeFilter = (filterName.equalsIgnoreCase("attachment.attach-time")
                        || filterName.equalsIgnoreCase("create-time"));
                valueCount = 1;
                do {
                    value = request.getParameter("Filter." + filterCount + ".Value." + valueCount);
                    if (null != value) {
                        // -> time values are not encoded as regexes
                        if (timeFilter)
                            nextFilter.addValue(value);
                        else
                            nextFilter.addValueEncoded(value);

                        valueCount++;
                    }
                } while (null != value);

                filterSet.add(nextFilter);
                filterCount++;
            }
        } while (null != filterName);

        if (1 == filterCount)
            return null;
        else
            return filterSet.toArray(new EC2Filter[0]);
    }

    private void describeKeyPairs(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        EC2DescribeKeyPairs ec2Request = new EC2DescribeKeyPairs();

        String[] keyNames = request.getParameterValues("KeyName");
        if (keyNames != null) {
            for (String keyName : keyNames) {
                ec2Request.addKeyName(keyName);
            }
        }
        EC2Filter[] filterSet = extractFilters(request);
        if (null != filterSet) {
            EC2KeyPairFilterSet vfs = new EC2KeyPairFilterSet();
            for (EC2Filter filter : filterSet) {
                vfs.addFilter(filter);
            }
            ec2Request.setKeyFilterSet(vfs);
        }

        DescribeKeyPairsResponse EC2Response = EC2SoapServiceImpl
                .toDescribeKeyPairs(ServiceProvider.getInstance().getEC2Engine().describeKeyPairs(ec2Request));
        serializeResponse(response, EC2Response);
    }

    private void importKeyPair(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {

        String keyName = request.getParameter("KeyName");
        String publicKeyMaterial = request.getParameter("PublicKeyMaterial");
        if (keyName == null && publicKeyMaterial == null) {
            response.sendError(530, "Missing parameter");
            return;
        }

        if (!publicKeyMaterial.contains(" "))
            publicKeyMaterial = new String(Base64.decodeBase64(publicKeyMaterial.getBytes()));

        EC2ImportKeyPair ec2Request = new EC2ImportKeyPair();
        if (ec2Request != null) {
            ec2Request.setKeyName(request.getParameter("KeyName"));
            ec2Request.setPublicKeyMaterial(request.getParameter("PublicKeyMaterial"));
        }

        ImportKeyPairResponse EC2Response = EC2SoapServiceImpl
                .toImportKeyPair(ServiceProvider.getInstance().getEC2Engine().importKeyPair(ec2Request));
        serializeResponse(response, EC2Response);
    }

    private void createKeyPair(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        String keyName = request.getParameter("KeyName");
        if (keyName == null) {
            response.sendError(530, "Missing KeyName parameter");
            return;
        }

        EC2CreateKeyPair ec2Request = new EC2CreateKeyPair();
        if (ec2Request != null) {
            ec2Request.setKeyName(keyName);
        }

        CreateKeyPairResponse EC2Response = EC2SoapServiceImpl
                .toCreateKeyPair(ServiceProvider.getInstance().getEC2Engine().createKeyPair(ec2Request));
        serializeResponse(response, EC2Response);
    }

    private void deleteKeyPair(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        String keyName = request.getParameter("KeyName");
        if (keyName == null) {
            response.sendError(530, "Missing KeyName parameter");
            return;
        }

        EC2DeleteKeyPair ec2Request = new EC2DeleteKeyPair();
        ec2Request.setKeyName(keyName);

        DeleteKeyPairResponse EC2Response = EC2SoapServiceImpl
                .toDeleteKeyPair(ServiceProvider.getInstance().getEC2Engine().deleteKeyPair(ec2Request));
        serializeResponse(response, EC2Response);
    }

    private void getPasswordData(HttpServletRequest request, HttpServletResponse response)
            throws ADBException, XMLStreamException, IOException {
        String instanceId = request.getParameter("InstanceId");
        if (instanceId == null) {
            response.sendError(530, "Missing InstanceId parameter");
            return;
        }

        GetPasswordDataResponse EC2Response = EC2SoapServiceImpl
                .toGetPasswordData(ServiceProvider.getInstance().getEC2Engine().getPasswordData(instanceId));
        serializeResponse(response, EC2Response);
    }

    /**
     * This function implements the EC2 REST authentication algorithm.   It uses the given
     * "AWSAccessKeyId" parameter to look up the Cloud.com account holder's secret key which is
     * used as input to the signature calculation.  In addition, it tests the given "Expires"
     * parameter to see if the signature has expired and if so the request fails.
     */
    private boolean authenticateRequest(HttpServletRequest request, HttpServletResponse response)
            throws SignatureException, IOException, InstantiationException, IllegalAccessException,
            ClassNotFoundException, SQLException, ParseException {
        String cloudSecretKey = null;
        String cloudAccessKey = null;
        String signature = null;
        String sigMethod = null;

        // [A] Basic parameters required for an authenticated rest request
        //  -> note that the Servlet engine will un-URL encode all parameters we extract via "getParameterValues()" calls
        String[] awsAccess = request.getParameterValues("AWSAccessKeyId");
        if (null != awsAccess && 0 < awsAccess.length)
            cloudAccessKey = awsAccess[0];
        else {
            response.sendError(530, "Missing AWSAccessKeyId parameter");
            return false;
        }

        String[] clientSig = request.getParameterValues("Signature");
        if (null != clientSig && 0 < clientSig.length)
            signature = clientSig[0];
        else {
            response.sendError(530, "Missing Signature parameter");
            return false;
        }

        String[] method = request.getParameterValues("SignatureMethod");
        if (null != method && 0 < method.length) {
            sigMethod = method[0];
            if (!sigMethod.equals("HmacSHA256") && !sigMethod.equals("HmacSHA1")) {
                response.sendError(531,
                        "Unsupported SignatureMethod value: " + sigMethod + " expecting: HmacSHA256 or HmacSHA1");
                return false;
            }
        } else {
            response.sendError(530, "Missing SignatureMethod parameter");
            return false;
        }

        String[] version = request.getParameterValues("Version");
        if (null != version && 0 < version.length) {
            if (!version[0].equals(wsdlVersion)) {
                response.sendError(531, "Unsupported Version value: " + version[0] + " expecting: " + wsdlVersion);
                return false;
            }
        } else {
            response.sendError(530, "Missing Version parameter");
            return false;
        }

        String[] sigVersion = request.getParameterValues("SignatureVersion");
        if (null != sigVersion && 0 < sigVersion.length) {
            if (!sigVersion[0].equals("2")) {
                response.sendError(531, "Unsupported SignatureVersion value: " + sigVersion[0] + " expecting: 2");
                return false;
            }
        } else {
            response.sendError(530, "Missing SignatureVersion parameter");
            return false;
        }

        // -> can have only one but not both { Expires | Timestamp } headers
        String[] expires = request.getParameterValues("Expires");
        if (null != expires && 0 < expires.length) {
            // -> contains the date and time at which the signature included in the request EXPIRES
            if (hasSignatureExpired(expires[0])) {
                response.sendError(531, "Expires parameter indicates signature has expired: " + expires[0]);
                return false;
            }
        } else { // -> contains the date and time at which the request is SIGNED
            String[] time = request.getParameterValues("Timestamp");
            if (null == time || 0 == time.length) {
                response.sendError(530, "Missing Timestamp and Expires parameter, one is required");
                return false;
            }
        }

        // [B] Use the cloudAccessKey to get the users secret key in the db
        UserCredentialsVO cloudKeys = ucDao.getByAccessKey(cloudAccessKey);

        if (null == cloudKeys) {
            logger.debug(cloudAccessKey + " is not defined in the EC2 service - call SetUserKeys");
            response.sendError(404, cloudAccessKey + " is not defined in the EC2 service - call SetUserKeys");
            return false;
        } else
            cloudSecretKey = cloudKeys.getSecretKey();

        // [C] Verify the signature
        //  -> getting the query-string in this way maintains its URL encoding
        EC2RestAuth restAuth = new EC2RestAuth();
        restAuth.setHostHeader(request.getHeader("Host"));
        String requestUri = request.getRequestURI();

        // If forwarded from another basepath:
        String forwardedPath = (String) request.getAttribute("javax.servlet.forward.request_uri");
        if (forwardedPath != null) {
            requestUri = forwardedPath;
        }
        restAuth.setHTTPRequestURI(requestUri);

        String queryString = request.getQueryString();
        // getQueryString returns null (does it ever NOT return null for these), 
        // we need to construct queryString to avoid changing the auth code...
        if (queryString == null) {
            // construct our idea of a queryString with parameters!
            Enumeration<?> params = request.getParameterNames();
            if (params != null) {
                while (params.hasMoreElements()) {
                    String paramName = (String) params.nextElement();
                    // exclude the signature string obviously. ;)
                    if (paramName.equalsIgnoreCase("Signature"))
                        continue;
                    if (queryString == null)
                        queryString = paramName + "=" + request.getParameter(paramName);
                    else
                        queryString = queryString + "&" + paramName + "="
                                + URLEncoder.encode(request.getParameter(paramName), "UTF-8");
                }
            }
        }
        restAuth.setQueryString(queryString);

        if (restAuth.verifySignature(request.getMethod(), cloudSecretKey, signature, sigMethod)) {
            UserContext.current().initContext(cloudAccessKey, cloudSecretKey, cloudAccessKey, "REST request", null);
            return true;
        } else
            throw new PermissionDeniedException("Invalid signature");
    }

    /**
     * We check this to reduce replay attacks.
     * 
     * @param timeStamp
     * @return true - if the request is not longer valid, false otherwise
     * @throws ParseException
     */
    private boolean hasSignatureExpired(String timeStamp) {
        Calendar cal = EC2RestAuth.parseDateString(timeStamp);
        if (null == cal)
            return false;

        Date expiredTime = cal.getTime();
        Date today = new Date(); // -> gets set to time of creation
        if (0 >= expiredTime.compareTo(today)) {
            logger.debug("timestamp given: [" + timeStamp + "], now: [" + today.toString() + "]");
            return true;
        } else
            return false;
    }

    private static void endResponse(HttpServletResponse response, String content) {
        try {
            byte[] data = content.getBytes();
            response.setContentLength(data.length);
            OutputStream os = response.getOutputStream();
            os.write(data);
            os.close();

        } catch (Throwable e) {
            logger.error("Unexpected exception " + e.getMessage(), e);
        }
    }

    private void logRequest(HttpServletRequest request) {
        if (logger.isInfoEnabled()) {
            logger.info("EC2 Request method: " + request.getMethod());
            logger.info("Request contextPath: " + request.getContextPath());
            logger.info("Request pathInfo: " + request.getPathInfo());
            logger.info("Request pathTranslated: " + request.getPathTranslated());
            logger.info("Request queryString: " + request.getQueryString());
            logger.info("Request requestURI: " + request.getRequestURI());
            logger.info("Request requestURL: " + request.getRequestURL());
            logger.info("Request servletPath: " + request.getServletPath());
            Enumeration<?> headers = request.getHeaderNames();
            if (headers != null) {
                while (headers.hasMoreElements()) {
                    Object headerName = headers.nextElement();
                    logger.info("Request header " + headerName + ":" + request.getHeader((String) headerName));
                }
            }

            Enumeration<?> params = request.getParameterNames();
            if (params != null) {
                while (params.hasMoreElements()) {
                    Object paramName = params.nextElement();
                    logger.info("Request parameter " + paramName + ":" + request.getParameter((String) paramName));
                }
            }
        }
    }

    /**
    * Send out an error response according to Amazon convention.
     */
    private void faultResponse(HttpServletResponse response, String errorCode, String errorMessage) {
        try {
            OutputStreamWriter out = new OutputStreamWriter(response.getOutputStream());
            response.setContentType("text/xml; charset=UTF-8");
            out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            out.write("<Response><Errors><Error><Code>");
            out.write(errorCode);
            out.write("</Code><Message>");
            out.write(errorMessage);
            out.write("</Message></Error></Errors><RequestID>");
            out.write(UUID.randomUUID().toString());
            out.write("</RequestID></Response>");
            out.flush();
            out.close();
        } catch (IOException e) {
            logger.error("Unexpected exception " + e.getMessage(), e);
        }
    }

    /**
    * Serialize Axis beans to XML output. 
     */
    private void serializeResponse(HttpServletResponse response, ADBBean EC2Response)
            throws ADBException, XMLStreamException, IOException {
        OutputStream os = response.getOutputStream();
        response.setStatus(200);
        response.setContentType("text/xml; charset=UTF-8");
        XMLStreamWriter xmlWriter = xmlOutFactory.createXMLStreamWriter(os);
        MTOMAwareXMLSerializer MTOMWriter = new MTOMAwareXMLSerializer(xmlWriter);
        MTOMWriter.setDefaultNamespace("http://ec2.amazonaws.com/doc/" + wsdlVersion + "/");
        EC2Response.serialize(null, factory, MTOMWriter);
        xmlWriter.flush();
        xmlWriter.close();
        os.close();
    }
}