Package com.cloud.bridge.service.controller.s3

Source Code of com.cloud.bridge.service.controller.s3.S3ObjectAction

// 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.controller.s3;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.UUID;

import javax.activation.DataHandler;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.DatatypeConverter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.amazon.s3.CopyObjectResponse;
import com.amazon.s3.GetObjectAccessControlPolicyResponse;

import com.cloud.bridge.io.MTOMAwareResultStreamWriter;
import com.cloud.bridge.model.SAcl;
import com.cloud.bridge.model.SAclVO;
import com.cloud.bridge.model.SBucketVO;
import com.cloud.bridge.persist.dao.MultipartLoadDao;
import com.cloud.bridge.persist.dao.SBucketDao;
import com.cloud.bridge.service.S3Constants;
import com.cloud.bridge.service.S3RestServlet;
import com.cloud.bridge.service.UserContext;
import com.cloud.bridge.service.core.s3.S3AccessControlList;
import com.cloud.bridge.service.core.s3.S3AccessControlPolicy;
import com.cloud.bridge.service.core.s3.S3AuthParams;
import com.cloud.bridge.service.core.s3.S3ConditionalHeaders;
import com.cloud.bridge.service.core.s3.S3CopyObjectRequest;
import com.cloud.bridge.service.core.s3.S3CopyObjectResponse;
import com.cloud.bridge.service.core.s3.S3DeleteObjectRequest;
import com.cloud.bridge.service.core.s3.S3Engine;
import com.cloud.bridge.service.core.s3.S3GetObjectAccessControlPolicyRequest;
import com.cloud.bridge.service.core.s3.S3GetObjectRequest;
import com.cloud.bridge.service.core.s3.S3GetObjectResponse;
import com.cloud.bridge.service.core.s3.S3Grant;
import com.cloud.bridge.service.core.s3.S3MetaDataEntry;
import com.cloud.bridge.service.core.s3.S3MultipartPart;
import com.cloud.bridge.service.core.s3.S3PolicyAction.PolicyActions;
import com.cloud.bridge.service.core.s3.S3PolicyContext;
import com.cloud.bridge.service.core.s3.S3PutObjectInlineRequest;
import com.cloud.bridge.service.core.s3.S3PutObjectInlineResponse;
import com.cloud.bridge.service.core.s3.S3Response;
import com.cloud.bridge.service.core.s3.S3SetObjectAccessControlPolicyRequest;
import com.cloud.bridge.service.exception.PermissionDeniedException;
import com.cloud.bridge.util.Converter;
import com.cloud.bridge.util.DateHelper;
import com.cloud.bridge.util.HeaderParam;
import com.cloud.bridge.util.OrderedPair;
import com.cloud.bridge.util.ServletRequestDataSource;

public class S3ObjectAction implements ServletAction {
    protected final static Logger logger = Logger.getLogger(S3ObjectAction.class);
    @Inject
    SBucketDao bucketDao;

    private DocumentBuilderFactory dbf = null;

    public S3ObjectAction() {
        dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);

    }

    @Override
    public void execute(HttpServletRequest request, HttpServletResponse response) throws IOException, XMLStreamException {
        String method = request.getMethod();
        String queryString = request.getQueryString();
        String copy = null;

        response.addHeader("x-amz-request-id", UUID.randomUUID().toString());

        if (method.equalsIgnoreCase("GET")) {
            if (queryString != null && queryString.length() > 0) {
                if (queryString.contains("acl"))
                    executeGetObjectAcl(request, response);
                else if (queryString.contains("uploadId"))
                    executeListUploadParts(request, response);
                else
                    executeGetObject(request, response);
            } else
                executeGetObject(request, response);
        } else if (method.equalsIgnoreCase("PUT")) {
            if (queryString != null && queryString.length() > 0) {
                if (queryString.contains("acl"))
                    executePutObjectAcl(request, response);
                else if (queryString.contains("partNumber"))
                    executeUploadPart(request, response);
                else
                    executePutObject(request, response);
            } else if (null != (copy = request.getHeader("x-amz-copy-source"))) {
                executeCopyObject(request, response, copy.trim());
            } else
                executePutObject(request, response);
        } else if (method.equalsIgnoreCase("DELETE")) {
            if (queryString != null && queryString.length() > 0) {
                if (queryString.contains("uploadId"))
                    executeAbortMultipartUpload(request, response);
                else
                    executeDeleteObject(request, response);
            } else
                executeDeleteObject(request, response);
        } else if (method.equalsIgnoreCase("HEAD")) {
            executeHeadObject(request, response);
        } else if (method.equalsIgnoreCase("POST")) {
            if (queryString != null && queryString.length() > 0) {
                if (queryString.contains("uploads"))
                    executeInitiateMultipartUpload(request, response);
                else if (queryString.contains("uploadId"))
                    executeCompleteMultipartUpload(request, response);
            } else if (request.getAttribute(S3Constants.PLAIN_POST_ACCESS_KEY) != null)
                executePlainPostObject(request, response);
            // TODO - Having implemented the request, now provide an informative HTML page response
            else
                executePostObject(request, response);
        } else
            throw new IllegalArgumentException("Unsupported method in REST request");
    }

    private void executeCopyObject(HttpServletRequest request, HttpServletResponse response, String copy) throws IOException, XMLStreamException {
        S3CopyObjectRequest engineRequest = new S3CopyObjectRequest();
        String versionId = null;

        String bucketName = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY);
        String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY);
        String sourceBucketName = null;
        String sourceKey = null;

        // [A] Parse the x-amz-copy-source header into usable pieces
        // Check to find a ?versionId= value if any
        int index = copy.indexOf('?');
        if (-1 != index) {
            versionId = copy.substring(index + 1);
            if (versionId.startsWith("versionId="))
                engineRequest.setVersion(versionId.substring(10));
            copy = copy.substring(0, index);
        }

        // The value of copy should look like: "bucket-name/object-name"
        index = copy.indexOf('/');

        // In case it looks like "/bucket-name/object-name" discard a leading '/' if it exists
        if (0 == index) {
            copy = copy.substring(1);
            index = copy.indexOf('/');
        }

        if (-1 == index)
            throw new IllegalArgumentException("Invalid x-amz-copy-source header value [" + copy + "]");

        sourceBucketName = copy.substring(0, index);
        sourceKey = copy.substring(index + 1);

        // [B] Set the object used in the SOAP request so it can do the bulk of the work for us
        engineRequest.setSourceBucketName(sourceBucketName);
        engineRequest.setSourceKey(sourceKey);
        engineRequest.setDestinationBucketName(bucketName);
        engineRequest.setDestinationKey(key);

        engineRequest.setDataDirective(request.getHeader("x-amz-metadata-directive"));
        engineRequest.setMetaEntries(extractMetaData(request));
        engineRequest.setCannedAccess(request.getHeader("x-amz-acl"));
        engineRequest.setConditions(conditionalRequest(request, true));

        // [C] Do the actual work and return the result
        S3CopyObjectResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest);

        versionId = engineResponse.getCopyVersion();
        if (null != versionId)
            response.addHeader("x-amz-copy-source-version-id", versionId);
        versionId = engineResponse.getPutVersion();
        if (null != versionId)
            response.addHeader("x-amz-version-id", versionId);

        // To allow the copy object result to be serialized via Axiom classes
        CopyObjectResponse allBuckets = S3SerializableServiceImplementation.toCopyObjectResponse(engineResponse);

        OutputStream outputStream = response.getOutputStream();
        response.setStatus(200);
        response.setContentType("application/xml");
        // The content-type literally should be "application/xml; charset=UTF-8"
        // but any compliant JVM supplies utf-8 by default;

        MTOMAwareResultStreamWriter resultWriter = new MTOMAwareResultStreamWriter("CopyObjectResult", outputStream);
        resultWriter.startWrite();
        resultWriter.writeout(allBuckets);
        resultWriter.stopWrite();

    }

    private void executeGetObjectAcl(HttpServletRequest request, HttpServletResponse response) throws IOException, XMLStreamException {
        String bucketName = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY);
        String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY);

        S3GetObjectAccessControlPolicyRequest engineRequest = new S3GetObjectAccessControlPolicyRequest();
        engineRequest.setBucketName(bucketName);
        engineRequest.setKey(key);

        // -> is this a request for a specific version of the object?  look for "versionId=" in the query string
        String queryString = request.getQueryString();
        if (null != queryString)
            engineRequest.setVersion(returnParameter(queryString, "versionId="));

        S3AccessControlPolicy engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest);
        int resultCode = engineResponse.getResultCode();
        if (200 != resultCode) {
            response.setStatus(resultCode);
            return;
        }
        String version = engineResponse.getVersion();
        if (null != version)
            response.addHeader("x-amz-version-id", version);

        // To allow the get object acl policy result to be serialized via Axiom classes
        GetObjectAccessControlPolicyResponse onePolicy = S3SerializableServiceImplementation.toGetObjectAccessControlPolicyResponse(engineResponse);

        OutputStream outputStream = response.getOutputStream();
        response.setStatus(200);
        response.setContentType("application/xml");
        // The content-type literally should be "application/xml; charset=UTF-8"
        // but any compliant JVM supplies utf-8 by default;

        MTOMAwareResultStreamWriter resultWriter = new MTOMAwareResultStreamWriter("GetObjectAccessControlPolicyResult", outputStream);
        resultWriter.startWrite();
        resultWriter.writeout(onePolicy);
        resultWriter.stopWrite();
    }

    private void executePutObjectAcl(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // [A] Determine that there is an applicable bucket which might have an ACL set

        String bucketName = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY);
        String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY);

        SBucketVO bucket = bucketDao.getByName(bucketName);
        String owner = null;
        if (null != bucket)
            owner = bucket.getOwnerCanonicalId();
        if (null == owner) {
            logger.error("ACL update failed since " + bucketName + " does not exist");
            throw new IOException("ACL update failed");
        }
        if (null == key) {
            logger.error("ACL update failed since " + bucketName + " does not contain the expected key");
            throw new IOException("ACL update failed");
        }

        // [B] Obtain the grant request which applies to the acl request string.  This latter is supplied as the value of the x-amz-acl header.

        S3SetObjectAccessControlPolicyRequest engineRequest = new S3SetObjectAccessControlPolicyRequest();
        S3Grant grantRequest = new S3Grant();
        S3AccessControlList aclRequest = new S3AccessControlList();

        String aclRequestString = request.getHeader("x-amz-acl");
        OrderedPair<Integer, Integer> accessControlsForObjectOwner = SAclVO.getCannedAccessControls(aclRequestString, "SObject");
        grantRequest.setPermission(accessControlsForObjectOwner.getFirst());
        grantRequest.setGrantee(accessControlsForObjectOwner.getSecond());
        grantRequest.setCanonicalUserID(owner);
        aclRequest.addGrant(grantRequest);
        engineRequest.setAcl(aclRequest);
        engineRequest.setBucketName(bucketName);
        engineRequest.setKey(key);

        // [C] Allow an S3Engine to handle the S3SetObjectAccessControlPolicyRequest
        S3Response engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest);
        response.setStatus(engineResponse.getResultCode());

    }

    private void executeGetObject(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String bucket = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY);
        String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY);

        S3GetObjectRequest engineRequest = new S3GetObjectRequest();
        engineRequest.setBucketName(bucket);
        engineRequest.setKey(key);
        engineRequest.setInlineData(true);
        engineRequest.setReturnData(true);
        //engineRequest.setReturnMetadata(true);
        engineRequest = setRequestByteRange(request, engineRequest);

        // -> is this a request for a specific version of the object?  look for "versionId=" in the query string
        String queryString = request.getQueryString();
        if (null != queryString)
            engineRequest.setVersion(returnParameter(queryString, "versionId="));

        S3GetObjectResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest);
        response.setStatus(engineResponse.getResultCode());

        if (engineResponse.getResultCode() >= 400) {
            return;
        }
        String deleteMarker = engineResponse.getDeleteMarker();
        if (null != deleteMarker) {
            response.addHeader("x-amz-delete-marker", "true");
            response.addHeader("x-amz-version-id", deleteMarker);
        } else {
            String version = engineResponse.getVersion();
            if (null != version)
                response.addHeader("x-amz-version-id", version);
        }

        // -> was the get conditional?
        if (!conditionPassed(request, response, engineResponse.getLastModified().getTime(), engineResponse.getETag()))
            return;

        // -> is there data to return
        // -> from the Amazon REST documentation it appears that Meta data is only returned as part of a HEAD request
        //returnMetaData( engineResponse, response );

        DataHandler dataHandler = engineResponse.getData();
        if (dataHandler != null) {
            response.addHeader("ETag", "\"" + engineResponse.getETag() + "\"");
            response.addHeader("Last-Modified",
                DateHelper.getDateDisplayString(DateHelper.GMT_TIMEZONE, engineResponse.getLastModified().getTime(), "E, d MMM yyyy HH:mm:ss z"));

            response.setContentLength((int)engineResponse.getContentLength());
            S3RestServlet.writeResponse(response, dataHandler.getInputStream());
        }
    }

    private void executePutObject(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String continueHeader = request.getHeader("Expect");
        if (continueHeader != null && continueHeader.equalsIgnoreCase("100-continue")) {
            S3RestServlet.writeResponse(response, "HTTP/1.1 100 Continue\r\n");
        }

        long contentLength = Converter.toLong(request.getHeader("Content-Length"), 0);

        String bucket = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY);
        String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY);
        S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest();
        engineRequest.setBucketName(bucket);
        engineRequest.setKey(key);
        engineRequest.setContentLength(contentLength);
        engineRequest.setMetaEntries(extractMetaData(request));
        engineRequest.setCannedAccess(request.getHeader("x-amz-acl"));

        DataHandler dataHandler = new DataHandler(new ServletRequestDataSource(request));
        engineRequest.setData(dataHandler);

        S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest);
        response.setHeader("ETag", "\"" + engineResponse.getETag() + "\"");
        String version = engineResponse.getVersion();
        if (null != version)
            response.addHeader("x-amz-version-id", version);
    }

    /**
     * Once versioining is turned on then to delete an object requires specifying a version
     * parameter.   A deletion marker is set once versioning is turned on in a bucket.
     */
    private void executeDeleteObject(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String bucket = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY);
        String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY);

        S3DeleteObjectRequest engineRequest = new S3DeleteObjectRequest();
        engineRequest.setBucketName(bucket);
        engineRequest.setKey(key);

        // -> is this a request for a specific version of the object?  look for "versionId=" in the query string
        String queryString = request.getQueryString();
        if (null != queryString)
            engineRequest.setVersion(returnParameter(queryString, "versionId="));

        S3Response engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest);

        response.setStatus(engineResponse.getResultCode());
        String version = engineRequest.getVersion();
        if (null != version)
            response.addHeader("x-amz-version-id", version);
    }

    /*
     * The purpose of a plain POST operation is to add an object to a specified bucket using HTML forms.
     * The capability is for developer and tester convenience providing a simple browser-based upload
     * feature as an alternative to using PUTs.
     * In the case of PUTs the upload information is passed through HTTP headers.  However in the case of a
     * POST this information must be supplied as form fields.  Many of these are mandatory or otherwise
     * the POST request will be rejected.
     * The requester using the HTML page must submit valid credentials sufficient for checking that
     * the bucket to which the object is to be added has WRITE permission for that user.  The AWS access
     * key field on the form is taken to be synonymous with the user canonical ID for this purpose.
     */
    private void executePlainPostObject(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String continueHeader = request.getHeader("Expect");
        if (continueHeader != null && continueHeader.equalsIgnoreCase("100-continue")) {
            S3RestServlet.writeResponse(response, "HTTP/1.1 100 Continue\r\n");
        }

        long contentLength = Converter.toLong(request.getHeader("Content-Length"), 0);

        String bucket = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY);
        String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY);
        String accessKey = (String)request.getAttribute(S3Constants.PLAIN_POST_ACCESS_KEY);
        String signature = (String)request.getAttribute(S3Constants.PLAIN_POST_SIGNATURE);
        S3Grant grant = new S3Grant();
        grant.setCanonicalUserID(accessKey);
        grant.setGrantee(SAcl.GRANTEE_USER);
        grant.setPermission(SAcl.PERMISSION_FULL);
        S3AccessControlList acl = new S3AccessControlList();
        acl.addGrant(grant);
        S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest();
        engineRequest.setBucketName(bucket);
        engineRequest.setKey(key);
        engineRequest.setAcl(acl);
        engineRequest.setContentLength(contentLength);
        engineRequest.setMetaEntries(extractMetaData(request));
        engineRequest.setCannedAccess(request.getHeader("x-amz-acl"));

        DataHandler dataHandler = new DataHandler(new ServletRequestDataSource(request));
        engineRequest.setData(dataHandler);

        S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest);
        response.setHeader("ETag", "\"" + engineResponse.getETag() + "\"");
        String version = engineResponse.getVersion();
        if (null != version)
            response.addHeader("x-amz-version-id", version);
    }

    private void executeHeadObject(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String bucket = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY);
        String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY);

        S3GetObjectRequest engineRequest = new S3GetObjectRequest();
        engineRequest.setBucketName(bucket);
        engineRequest.setKey(key);
        engineRequest.setInlineData(true);    // -> need to set so we get ETag etc returned
        engineRequest.setReturnData(true);
        engineRequest.setReturnMetadata(true);
        engineRequest = setRequestByteRange(request, engineRequest);

        // -> is this a request for a specific version of the object?  look for "versionId=" in the query string
        String queryString = request.getQueryString();
        if (null != queryString)
            engineRequest.setVersion(returnParameter(queryString, "versionId="));

        S3GetObjectResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest);
        response.setStatus(engineResponse.getResultCode());

        //bucket lookup for non-existance key

        if (engineResponse.getResultCode() == 404)
            return;

        String deleteMarker = engineResponse.getDeleteMarker();
        if (null != deleteMarker) {
            response.addHeader("x-amz-delete-marker", "true");
            response.addHeader("x-amz-version-id", deleteMarker);
        } else {
            String version = engineResponse.getVersion();
            if (null != version)
                response.addHeader("x-amz-version-id", version);
        }

        // -> was the head request conditional?
        if (!conditionPassed(request, response, engineResponse.getLastModified().getTime(), engineResponse.getETag()))
            return;

        // -> for a head request we return everything except the data
        returnMetaData(engineResponse, response);

        DataHandler dataHandler = engineResponse.getData();
        if (dataHandler != null) {
            response.addHeader("ETag", "\"" + engineResponse.getETag() + "\"");
            response.addHeader("Last-Modified",
                DateHelper.getDateDisplayString(DateHelper.GMT_TIMEZONE, engineResponse.getLastModified().getTime(), "E, d MMM yyyy HH:mm:ss z"));

            response.setContentLength((int)engineResponse.getContentLength());
        }
    }

    // There is a problem with POST since the 'Signature' and 'AccessKey' parameters are not
    // determined until we hit this function (i.e., they are encoded in the body of the message
    // they are not HTTP request headers).  All the values we used to get in the request headers
    // are not encoded in the request body.
    //
    // add ETag header computed as Base64 MD5 whenever object is uploaded or updated
    //
    private void executePostObject(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String bucket = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY);
        String contentType = request.getHeader("Content-Type");
        int boundaryIndex = contentType.indexOf("boundary=");
        String boundary = "--" + (contentType.substring(boundaryIndex + 9));
        String lastBoundary = boundary + "--";

        InputStreamReader isr = new InputStreamReader(request.getInputStream());
        BufferedReader br = new BufferedReader(isr);

        StringBuffer temp = new StringBuffer();
        String oneLine = null;
        String name = null;
        String value = null;
        String metaName = null;   // -> after stripped off the x-amz-meta-
        boolean isMetaTag = false;
        int countMeta = 0;
        int state = 0;

        // [A] First parse all the parts out of the POST request and message body
        // -> bucket name is still encoded in a Host header
        S3AuthParams params = new S3AuthParams();
        List<S3MetaDataEntry> metaSet = new ArrayList<S3MetaDataEntry>();
        S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest();
        engineRequest.setBucketName(bucket);

        // -> the last body part contains the content that is used to write the S3 object, all
        //    other body parts are header values
        while (null != (oneLine = br.readLine())) {
            if (oneLine.startsWith(lastBoundary)) {
                // -> this is the data of the object to put
                if (0 < temp.length()) {
                    value = temp.toString();
                    temp.setLength(0);

                    engineRequest.setContentLength(value.length());
                    engineRequest.setDataAsString(value);
                }
                break;
            } else if (oneLine.startsWith(boundary)) {
                // -> this is the header data
                if (0 < temp.length()) {
                    value = temp.toString().trim();
                    temp.setLength(0);
                    //System.out.println( "param: " + name + " = " + value );

                    if (name.equalsIgnoreCase("key")) {
                        engineRequest.setKey(value);
                    } else if (name.equalsIgnoreCase("x-amz-acl")) {
                        engineRequest.setCannedAccess(value);
                    } else if (isMetaTag) {
                        S3MetaDataEntry oneMeta = new S3MetaDataEntry();
                        oneMeta.setName(metaName);
                        oneMeta.setValue(value);
                        metaSet.add(oneMeta);
                        countMeta++;
                        metaName = null;
                    }

                    // -> build up the headers so we can do authentication on this POST
                    HeaderParam oneHeader = new HeaderParam();
                    oneHeader.setName(name);
                    oneHeader.setValue(value);
                    params.addHeader(oneHeader);
                }
                state = 1;
            } else if (1 == state && 0 == oneLine.length()) {
                // -> data of a body part starts here
                state = 2;
            } else if (1 == state) {
                // -> the name of the 'name-value' pair is encoded in the Content-Disposition header
                if (oneLine.startsWith("Content-Disposition: form-data;")) {
                    isMetaTag = false;
                    int nameOffset = oneLine.indexOf("name=");
                    if (-1 != nameOffset) {
                        name = oneLine.substring(nameOffset + 5);
                        if (name.startsWith("\""))
                            name = name.substring(1);
                        if (name.endsWith("\""))
                            name = name.substring(0, name.length() - 1);
                        name = name.trim();

                        if (name.startsWith("x-amz-meta-")) {
                            metaName = name.substring(11);
                            isMetaTag = true;
                        }
                    }
                }
            } else if (2 == state) {
                // -> the body parts data may take up multiple lines
                //System.out.println( oneLine.length() + " body data: " + oneLine );
                temp.append(oneLine);
            }
//            else System.out.println( oneLine.length() + " preamble: " + oneLine );
        }

        // [B] Authenticate the POST request after we have all the headers
        try {
            S3RestServlet.authenticateRequest(request, params);
        } catch (Exception e) {
            throw new IOException(e.toString());
        }

        // [C] Perform the request
        if (0 < countMeta)
            engineRequest.setMetaEntries(metaSet.toArray(new S3MetaDataEntry[0]));
        S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest);
        response.setHeader("ETag", "\"" + engineResponse.getETag() + "\"");
        String version = engineResponse.getVersion();
        if (null != version)
            response.addHeader("x-amz-version-id", version);
    }

    /**
     * Save all the information about the multipart upload request in the database so once it is finished
     * (in the future) we can create the real S3 object.
     *
     * @throws IOException
     */
    private void executeInitiateMultipartUpload(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // This request is via a POST which typically has its auth parameters inside the message
        try {
            S3RestServlet.authenticateRequest(request, S3RestServlet.extractRequestHeaders(request));
        } catch (Exception e) {
            throw new IOException(e.toString());
        }

        String bucket = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY);
        String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY);
        String cannedAccess = request.getHeader("x-amz-acl");
        S3MetaDataEntry[] meta = extractMetaData(request);

        // -> the S3 engine has easy access to all the privileged checking code
        S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest();
        engineRequest.setBucketName(bucket);
        engineRequest.setKey(key);
        engineRequest.setCannedAccess(cannedAccess);
        engineRequest.setMetaEntries(meta);
        S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().initiateMultipartUpload(engineRequest);
        int result = engineResponse.getResultCode();
        response.setStatus(result);
        if (200 != result)
            return;

        // -> there is no SOAP version of this function
        StringBuffer xml = new StringBuffer();
        xml.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
        xml.append("<InitiateMultipartUploadResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">");
        xml.append("<Bucket>").append(bucket).append("</Bucket>");
        xml.append("<Key>").append(key).append("</Key>");
        xml.append("<UploadId>").append(engineResponse.getUploadId()).append("</UploadId>");
        xml.append("</InitiateMultipartUploadResult>");

        response.setContentType("text/xml; charset=UTF-8");
        S3RestServlet.endResponse(response, xml.toString());
    }

    private void executeUploadPart(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String continueHeader = request.getHeader("Expect");
        if (continueHeader != null && continueHeader.equalsIgnoreCase("100-continue")) {
            S3RestServlet.writeResponse(response, "HTTP/1.1 100 Continue\r\n");
        }

        String bucket = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY);
        String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY);
        int partNumber = -1;
        int uploadId = -1;

        long contentLength = Converter.toLong(request.getHeader("Content-Length"), 0);

        String temp = request.getParameter("uploadId");
        if (null != temp)
            uploadId = Integer.parseInt(temp);

        temp = request.getParameter("partNumber");
        if (null != temp)
            partNumber = Integer.parseInt(temp);
        if (partNumber < 1 || partNumber > 10000) {
            logger.error("uploadPart invalid part number " + partNumber);
            response.setStatus(416);
            return;
        }

        // -> verification
        try {
            MultipartLoadDao uploadDao = new MultipartLoadDao();
            if (null == uploadDao.multipartExits(uploadId)) {
                response.setStatus(404);
                return;
            }

            // -> another requirement is that only the upload initiator can upload parts
            String initiator = uploadDao.getInitiator(uploadId);
            if (null == initiator || !initiator.equals(UserContext.current().getAccessKey())) {
                response.setStatus(403);
                return;
            }
        } catch (Exception e) {
            logger.error("executeUploadPart failed due to " + e.getMessage(), e);
            response.setStatus(500);
            return;
        }

        S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest();
        engineRequest.setBucketName(bucket);
        engineRequest.setKey(key);
        engineRequest.setContentLength(contentLength);
        DataHandler dataHandler = new DataHandler(new ServletRequestDataSource(request));
        engineRequest.setData(dataHandler);

        S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().saveUploadPart(engineRequest, uploadId, partNumber);
        if (null != engineResponse.getETag())
            response.setHeader("ETag", "\"" + engineResponse.getETag() + "\"");
        response.setStatus(engineResponse.getResultCode());
    }

    /**
     * This function is required to both parsing XML on the request and return XML as part of its result.
     *
     * @param request
     * @param response
     * @throws IOException
     */
    private void executeCompleteMultipartUpload(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // [A] This request is via a POST which typically has its auth parameters inside the message
        try {
            S3RestServlet.authenticateRequest(request, S3RestServlet.extractRequestHeaders(request));
        } catch (Exception e) {
            throw new IOException(e.toString());
        }

        String bucket = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY);
        String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY);
        S3MultipartPart[] parts = null;
        S3MetaDataEntry[] meta = null;
        String cannedAccess = null;
        int uploadId = -1;

        //  AWS S3 specifies that the keep alive connection is by sending whitespace characters until done
        // Therefore the XML version prolog is prepended to the stream in advance
        OutputStream outputStream = response.getOutputStream();
        outputStream.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>".getBytes());

        String temp = request.getParameter("uploadId");
        if (null != temp)
            uploadId = Integer.parseInt(temp);

        // [B] Look up all the uploaded body parts and related info
        try {
            MultipartLoadDao uploadDao = new MultipartLoadDao();
            if (null == uploadDao.multipartExits(uploadId)) {
                response.setStatus(404);
                returnErrorXML(404, "NotFound", outputStream);
                return;
            }

            // -> another requirement is that only the upload initiator can upload parts
            String initiator = uploadDao.getInitiator(uploadId);
            if (null == initiator || !initiator.equals(UserContext.current().getAccessKey())) {
                response.setStatus(403);
                returnErrorXML(403, "Forbidden", outputStream);
                return;
            }

            parts = uploadDao.getParts(uploadId, 10000, 0);
            meta = uploadDao.getMeta(uploadId);
            cannedAccess = uploadDao.getCannedAccess(uploadId);
        } catch (Exception e) {
            logger.error("executeCompleteMultipartUpload failed due to " + e.getMessage(), e);
            response.setStatus(500);
            returnErrorXML(500, "InternalError", outputStream);
            return;
        }

        // [C] Parse the given XML body part and perform error checking
        OrderedPair<Integer, String> match = verifyParts(request.getInputStream(), parts);
        if (200 != match.getFirst().intValue()) {
            response.setStatus(match.getFirst().intValue());
            returnErrorXML(match.getFirst().intValue(), match.getSecond(), outputStream);
            return;
        }

        // [D] Ask the engine to create a newly re-constituted object
        S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest();
        engineRequest.setBucketName(bucket);
        engineRequest.setKey(key);
        engineRequest.setMetaEntries(meta);
        engineRequest.setCannedAccess(cannedAccess);

        S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().concatentateMultipartUploads(response, engineRequest, parts, outputStream);
        int result = engineResponse.getResultCode();
        // -> free all multipart state since we now have one concatentated object
        if (200 == result)
            ServiceProvider.getInstance().getS3Engine().freeUploadParts(bucket, uploadId, false);

        // If all successful then clean up all left over parts
        // Notice that "<?xml version=\"1.0\" encoding=\"utf-8\"?>" has already been written into the servlet output stream at the beginning of section [A]
        if (200 == result) {
            StringBuffer xml = new StringBuffer();
            xml.append("<CompleteMultipartUploadResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">");
            xml.append("<Location>").append("http://" + bucket + ".s3.amazonaws.com/" + key).append("</Location>");
            xml.append("<Bucket>").append(bucket).append("</Bucket>");
            xml.append("<Key>").append(key).append("</Key>");
            xml.append("<ETag>\"").append(engineResponse.getETag()).append("\"</ETag>");
            xml.append("</CompleteMultipartUploadResult>");
            String xmlString = xml.toString().replaceAll("^\\s+", "");   // Remove leading whitespace characters
            outputStream.write(xmlString.getBytes());
            outputStream.close();
        } else
            returnErrorXML(result, null, outputStream);
    }

    private void executeAbortMultipartUpload(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String bucket = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY);
        int uploadId = -1;

        String temp = request.getParameter("uploadId");
        if (null != temp)
            uploadId = Integer.parseInt(temp);

        int result = ServiceProvider.getInstance().getS3Engine().freeUploadParts(bucket, uploadId, true);
        response.setStatus(result);
    }

    private void executeListUploadParts(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String bucketName = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY);
        String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY);
        String owner = null;
        String initiator = null;
        S3MultipartPart[] parts = null;
        int remaining = 0;
        int uploadId = -1;
        int maxParts = 1000;
        int partMarker = 0;
        int nextMarker = 0;

        String temp = request.getParameter("uploadId");
        if (null != temp)
            uploadId = Integer.parseInt(temp);

        temp = request.getParameter("max-parts");
        if (null != temp) {
            maxParts = Integer.parseInt(temp);
            if (maxParts > 1000 || maxParts < 0)
                maxParts = 1000;
        }

        temp = request.getParameter("part-number-marker");
        if (null != temp)
            partMarker = Integer.parseInt(temp);

        // -> does the bucket exist, we may need it to verify access permissions
        SBucketVO bucket = bucketDao.getByName(bucketName);
        if (bucket == null) {
            logger.error("listUploadParts failed since " + bucketName + " does not exist");
            response.setStatus(404);
            return;
        }

        try {
            MultipartLoadDao uploadDao = new MultipartLoadDao();
            OrderedPair<String, String> exists = uploadDao.multipartExits(uploadId);
            if (null == exists) {
                response.setStatus(404);
                return;
            }
            owner = exists.getFirst();

            // -> the multipart initiator or bucket owner can do this action
            initiator = uploadDao.getInitiator(uploadId);
            if (null == initiator || !initiator.equals(UserContext.current().getAccessKey())) {
                try {
                    // -> write permission on a bucket allows a PutObject / DeleteObject action on any object in the bucket
                    S3PolicyContext context = new S3PolicyContext(PolicyActions.ListMultipartUploadParts, bucketName);
                    context.setKeyName(exists.getSecond());
                    S3Engine.verifyAccess(context, "SBucket", bucket.getId(), SAcl.PERMISSION_WRITE);
                } catch (PermissionDeniedException e) {
                    response.setStatus(403);
                    return;
                }
            }

            parts = uploadDao.getParts(uploadId, maxParts, partMarker);
            remaining = uploadDao.numParts(uploadId, partMarker + maxParts);
        } catch (Exception e) {
            logger.error("List Uploads failed due to " + e.getMessage(), e);
            response.setStatus(500);
        }

        StringBuffer xml = new StringBuffer();
        xml.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
        xml.append("<ListPartsResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">");
        xml.append("<Bucket>").append(bucket).append("</Bucket>");
        xml.append("<Key>").append(key).append("</Key>");
        xml.append("<UploadId>").append(uploadId).append("</UploadId>");

        // -> currently we just have the access key and have no notion of a display name
        xml.append("<Initiator>");
        xml.append("<ID>").append(initiator).append("</ID>");
        xml.append("<DisplayName></DisplayName>");
        xml.append("</Initiator>");
        xml.append("<Owner>");
        xml.append("<ID>").append(owner).append("</ID>");
        xml.append("<DisplayName></DisplayName>");
        xml.append("</Owner>");

        StringBuffer partsList = new StringBuffer();
        for (int i = 0; i < parts.length; i++) {
            S3MultipartPart onePart = parts[i];
            if (null == onePart)
                break;

            nextMarker = onePart.getPartNumber();
            partsList.append("<Part>");
            partsList.append("<PartNumber>").append(nextMarker).append("</PartNumber>");
            partsList.append("<LastModified>").append(DatatypeConverter.printDateTime(onePart.getLastModified())).append("</LastModified>");
            partsList.append("<ETag>\"").append(onePart.getETag()).append("\"</ETag>");
            partsList.append("<Size>").append(onePart.getSize()).append("</Size>");
            partsList.append("</Part>");
        }

        xml.append("<StorageClass>STANDARD</StorageClass>");
        xml.append("<PartNumberMarker>").append(partMarker).append("</PartNumberMarker>");
        xml.append("<NextPartNumberMarker>").append(nextMarker).append("</NextPartNumberMarker>");
        xml.append("<MaxParts>").append(maxParts).append("</MaxParts>");
        xml.append("<IsTruncated>").append((0 < remaining ? "true" : "false")).append("</IsTruncated>");

        xml.append(partsList.toString());
        xml.append("</ListPartsResult>");

        response.setStatus(200);
        response.setContentType("text/xml; charset=UTF-8");
        S3RestServlet.endResponse(response, xml.toString());
    }

    /**
     * Support the "Range: bytes=0-399" header with just one byte range.
     * @param request
     * @param engineRequest
     * @return
     */
    private S3GetObjectRequest setRequestByteRange(HttpServletRequest request, S3GetObjectRequest engineRequest) {
        String temp = request.getHeader("Range");
        if (null == temp)
            return engineRequest;

        int offset = temp.indexOf("=");
        if (-1 != offset) {
            String range = temp.substring(offset + 1);

            String[] parts = range.split("-");
            if (2 >= parts.length) {
                // -> the end byte is inclusive
                engineRequest.setByteRangeStart(Long.parseLong(parts[0]));
                engineRequest.setByteRangeEnd(Long.parseLong(parts[1]) + 1);
            }
        }
        return engineRequest;
    }

    private S3ConditionalHeaders conditionalRequest(HttpServletRequest request, boolean isCopy) {
        S3ConditionalHeaders headers = new S3ConditionalHeaders();

        if (isCopy) {
            headers.setModifiedSince(request.getHeader("x-amz-copy-source-if-modified-since"));
            headers.setUnModifiedSince(request.getHeader("x-amz-copy-source-if-unmodified-since"));
            headers.setMatch(request.getHeader("x-amz-copy-source-if-match"));
            headers.setNoneMatch(request.getHeader("x-amz-copy-source-if-none-match"));
        } else {
            headers.setModifiedSince(request.getHeader("If-Modified-Since"));
            headers.setUnModifiedSince(request.getHeader("If-Unmodified-Since"));
            headers.setMatch(request.getHeader("If-Match"));
            headers.setNoneMatch(request.getHeader("If-None-Match"));
        }
        return headers;
    }

    private boolean conditionPassed(HttpServletRequest request, HttpServletResponse response, Date lastModified, String ETag) {
        S3ConditionalHeaders ifCond = conditionalRequest(request, false);

        if (0 > ifCond.ifModifiedSince(lastModified)) {
            response.setStatus(304);
            return false;
        }
        if (0 > ifCond.ifUnmodifiedSince(lastModified)) {
            response.setStatus(412);
            return false;
        }
        if (0 > ifCond.ifMatchEtag(ETag)) {
            response.setStatus(412);
            return false;
        }
        if (0 > ifCond.ifNoneMatchEtag(ETag)) {
            response.setStatus(412);
            return false;
        }
        return true;
    }

    /**
     * Return the saved object's meta data back to the client as HTTP "x-amz-meta-" headers.
     * This function is constructing an HTTP header and these headers have a defined syntax
     * as defined in rfc2616.   Any characters that could cause an invalid HTTP header will
     * prevent that meta data from being returned via the REST call (as is defined in the Amazon
     * spec).   These characters can be defined if using the SOAP API as well as the REST API.
     *
     * @param engineResponse
     * @param response
     */
    private void returnMetaData(S3GetObjectResponse engineResponse, HttpServletResponse response) {
        boolean ignoreMeta = false;
        int ignoredCount = 0;

        S3MetaDataEntry[] metaSet = engineResponse.getMetaEntries();
        for (int i = 0; null != metaSet && i < metaSet.length; i++) {
            String name = metaSet[i].getName();
            String value = metaSet[i].getValue();
            byte[] nameBytes = name.getBytes();
            ignoreMeta = false;

            // -> cannot have control characters (octets 0 - 31) and DEL (127), in an HTTP header
            for (int j = 0; j < name.length(); j++) {
                if ((0 <= nameBytes[j] && 31 >= nameBytes[j]) || 127 == nameBytes[j]) {
                    ignoreMeta = true;
                    break;
                }
            }

            // -> cannot have HTTP separators in an HTTP header
            if (-1 != name.indexOf('(') || -1 != name.indexOf(')') || -1 != name.indexOf('@') || -1 != name.indexOf('<') || -1 != name.indexOf('>') ||
                -1 != name.indexOf('\"') || -1 != name.indexOf('[') || -1 != name.indexOf(']') || -1 != name.indexOf('=') || -1 != name.indexOf(',') ||
                -1 != name.indexOf(';') || -1 != name.indexOf(':') || -1 != name.indexOf('\\') || -1 != name.indexOf('/') || -1 != name.indexOf(' ') ||
                -1 != name.indexOf('{') || -1 != name.indexOf('}') || -1 != name.indexOf('?') || -1 != name.indexOf('\t'))
                ignoreMeta = true;

            if (ignoreMeta)
                ignoredCount++;
            else
                response.addHeader("x-amz-meta-" + name, value);
        }

        if (0 < ignoredCount)
            response.addHeader("x-amz-missing-meta", new String("" + ignoredCount));
    }

    /**
     * Extract the name and value of all meta data so it can be written with the
     * object that is being 'PUT'.
     *
     * @param request
     * @return
     */
    private S3MetaDataEntry[] extractMetaData(HttpServletRequest request) {
        List<S3MetaDataEntry> metaSet = new ArrayList<S3MetaDataEntry>();
        int count = 0;

        Enumeration headers = request.getHeaderNames();
        while (headers.hasMoreElements()) {
            String key = (String)headers.nextElement();
            if (key.startsWith("x-amz-meta-")) {
                String name = key.substring(11);
                String value = request.getHeader(key);
                if (null != value) {
                    S3MetaDataEntry oneMeta = new S3MetaDataEntry();
                    oneMeta.setName(name);
                    oneMeta.setValue(value);
                    metaSet.add(oneMeta);
                    count++;
                }
            }
        }

        if (0 < count)
            return metaSet.toArray(new S3MetaDataEntry[0]);
        else
            return null;
    }

    /**
     * Parameters on the query string may or may not be name-value pairs.
     * For example:  "?acl&versionId=2", notice that "acl" has no value other
     * than it is present.
     *
     * @param queryString - from a URL to locate the 'find' parameter
     * @param find        - name string to return first found
     * @return the value matching the found name
     */
    private String returnParameter(String queryString, String find) {
        int offset = queryString.indexOf(find);
        if (-1 != offset) {
            String temp = queryString.substring(offset);
            String[] paramList = temp.split("[&=]");
            if (null != paramList && 2 <= paramList.length)
                return paramList[1];
        }
        return null;
    }

    private void returnErrorXML(int errorCode, String errorDescription, OutputStream os) throws IOException {
        StringBuffer xml = new StringBuffer();

        xml.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
        xml.append("<Error>");

        if (null != errorDescription)
            xml.append("<Code>").append(errorDescription).append("</Code>");
        else
            xml.append("<Code>").append(errorCode).append("</Code>");

        xml.append("<Message>").append("").append("</Message>");
        xml.append("<RequestId>").append("").append("</RequestId>");
        xml.append("<HostId>").append("").append("</<HostId>");
        xml.append("</Error>");

        os.write(xml.toString().getBytes());
        os.close();
    }

    /**
     * The Complete Multipart Upload function pass in the request body a list of
     * all uploaded body parts.   It is required that we verify that list matches
     * what was uploaded.
     *
     * @param is
     * @param parts
     * @return error code, and error string
     * @throws ParserConfigurationException, IOException, SAXException
     */
    private OrderedPair<Integer, String> verifyParts(InputStream is, S3MultipartPart[] parts) {
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);

            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(is);
            Node parent = null;
            Node contents = null;
            NodeList children = null;
            String temp = null;
            String element = null;
            String eTag = null;
            int lastNumber = -1;
            int partNumber = -1;
            int count = 0;

            // -> handle with and without a namespace
            NodeList nodeSet = doc.getElementsByTagNameNS("http://s3.amazonaws.com/doc/2006-03-01/", "Part");
            count = nodeSet.getLength();
            if (0 == count) {
                nodeSet = doc.getElementsByTagName("Part");
                count = nodeSet.getLength();
            }
            if (count != parts.length)
                return new OrderedPair<Integer, String>(400, "InvalidPart");

            // -> get a list of all the children elements of the 'Part' parent element
            for (int i = 0; i < count; i++) {
                partNumber = -1;
                eTag = null;
                parent = nodeSet.item(i);

                if (null != (children = parent.getChildNodes())) {
                    int numChildren = children.getLength();
                    for (int j = 0; j < numChildren; j++) {
                        contents = children.item(j);
                        element = contents.getNodeName().trim();
                        if (element.endsWith("PartNumber")) {
                            temp = contents.getFirstChild().getNodeValue();
                            if (null != temp)
                                partNumber = Integer.parseInt(temp);
                            //System.out.println( "part: " + partNumber );
                        } else if (element.endsWith("ETag")) {
                            eTag = contents.getFirstChild().getNodeValue();
                            //System.out.println( "etag: " + eTag );
                        }
                    }
                }

                // -> do the parts given in the call XML match what was previously uploaded?
                if (lastNumber >= partNumber) {
                    return new OrderedPair<Integer, String>(400, "InvalidPartOrder");
                }
                if (partNumber != parts[i].getPartNumber() || eTag == null || !eTag.equalsIgnoreCase("\"" + parts[i].getETag() + "\"")) {
                    return new OrderedPair<Integer, String>(400, "InvalidPart");
                }

                lastNumber = partNumber;
            }
            return new OrderedPair<Integer, String>(200, "Success");
        } catch (Exception e) {
            return new OrderedPair<Integer, String>(500, e.toString());
        }
    }
}
TOP

Related Classes of com.cloud.bridge.service.controller.s3.S3ObjectAction

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.