com.eucalyptus.objectstorage.pipeline.binding.ObjectStorageRESTBinding.java Source code

Java tutorial

Introduction

Here is the source code for com.eucalyptus.objectstorage.pipeline.binding.ObjectStorageRESTBinding.java

Source

/*************************************************************************
 * Copyright 2009-2013 Eucalyptus Systems, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses/.
 *
 * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
 * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
 * additional information or have any questions.
 *
 * This file may incorporate work covered under the following copyright
 * and permission notice:
 *
 *   Software License Agreement (BSD License)
 *
 *   Copyright (c) 2008, Regents of the University of California
 *   All rights reserved.
 *
 *   Redistribution and use of this software in source and binary forms,
 *   with or without modification, are permitted provided that the
 *   following conditions are met:
 *
 *     Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *     Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer
 *     in the documentation and/or other materials provided with the
 *     distribution.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *   COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *   POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE
 *   THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL,
 *   COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE,
 *   AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
 *   IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA,
 *   SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY,
 *   WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION,
 *   REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO
 *   IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT
 *   NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS.
 ************************************************************************/

package com.eucalyptus.objectstorage.pipeline.binding;

import com.eucalyptus.auth.policy.key.Iso8601DateParser;
import com.eucalyptus.auth.principal.User;
import com.eucalyptus.binding.Binding;
import com.eucalyptus.binding.BindingException;
import com.eucalyptus.binding.BindingManager;
import com.eucalyptus.binding.HttpEmbedded;
import com.eucalyptus.binding.HttpParameterMapping;
import com.eucalyptus.context.Contexts;
import com.eucalyptus.http.MappingHttpRequest;
import com.eucalyptus.http.MappingHttpResponse;
import com.eucalyptus.objectstorage.ObjectStorageBucketLogger;
import com.eucalyptus.objectstorage.exceptions.s3.InvalidArgumentException;
import com.eucalyptus.objectstorage.exceptions.s3.MalformedACLErrorException;
import com.eucalyptus.objectstorage.exceptions.s3.MalformedPOSTRequestException;
import com.eucalyptus.objectstorage.exceptions.s3.MalformedXMLException;
import com.eucalyptus.objectstorage.exceptions.s3.MethodNotAllowedException;
import com.eucalyptus.objectstorage.exceptions.s3.NotImplementedException;
import com.eucalyptus.objectstorage.exceptions.s3.S3Exception;
import com.eucalyptus.objectstorage.msgs.ObjectStorageDataGetRequestType;
import com.eucalyptus.objectstorage.msgs.ObjectStorageDataRequestType;
import com.eucalyptus.objectstorage.msgs.ObjectStorageRequestType;
import com.eucalyptus.objectstorage.pipeline.ObjectStorageRESTPipeline;
import com.eucalyptus.objectstorage.pipeline.handlers.ObjectStorageAuthenticationHandler;
import com.eucalyptus.objectstorage.util.AclUtils;
import com.eucalyptus.objectstorage.util.OSGUtil;
import com.eucalyptus.objectstorage.util.ObjectStorageProperties;
import com.eucalyptus.records.Logs;
import com.eucalyptus.storage.common.DateFormatter;
import com.eucalyptus.storage.msgs.BucketLogData;
import com.eucalyptus.storage.msgs.s3.AccessControlList;
import com.eucalyptus.storage.msgs.s3.AccessControlPolicy;
import com.eucalyptus.storage.msgs.s3.CanonicalUser;
import com.eucalyptus.storage.msgs.s3.Expiration;
import com.eucalyptus.storage.msgs.s3.Grant;
import com.eucalyptus.storage.msgs.s3.Grantee;
import com.eucalyptus.storage.msgs.s3.Group;
import com.eucalyptus.storage.msgs.s3.LifecycleConfiguration;
import com.eucalyptus.storage.msgs.s3.LifecycleRule;
import com.eucalyptus.storage.msgs.s3.LoggingEnabled;
import com.eucalyptus.storage.msgs.s3.MetaDataEntry;
import com.eucalyptus.storage.msgs.s3.Part;
import com.eucalyptus.storage.msgs.s3.TargetGrants;
import com.eucalyptus.storage.msgs.s3.Transition;
import com.eucalyptus.util.ChannelBufferStreamingInputStream;
import com.eucalyptus.util.LogUtil;
import com.eucalyptus.util.XMLParser;
import com.eucalyptus.ws.handlers.RestfulMarshallingHandler;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import edu.ucsb.eucalyptus.msgs.BaseMessage;
import edu.ucsb.eucalyptus.msgs.EucalyptusErrorMessageType;
import edu.ucsb.eucalyptus.msgs.ExceptionResponseType;
import groovy.lang.GroovyObject;
import org.apache.axiom.om.OMElement;
import org.apache.commons.httpclient.util.DateUtil;
import org.apache.log4j.Logger;
import org.apache.tools.ant.util.DateUtils;
import org.apache.xml.dtm.ref.DTMNodeList;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelEvent;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.w3c.dom.Node;

import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

public abstract class ObjectStorageRESTBinding extends RestfulMarshallingHandler {
    protected static Logger LOG = Logger.getLogger(ObjectStorageRESTBinding.class);
    protected static final String SERVICE = "service";
    protected static final String BUCKET = "bucket";
    protected static final String OBJECT = "object";
    protected Map<String, String> operationMap;
    protected Map<String, String> unsupportedOperationMap;
    protected String key;

    public ObjectStorageRESTBinding() {
        super("http://s3.amazonaws.com/doc/" + ObjectStorageProperties.NAMESPACE_VERSION);
        operationMap = populateOperationMap();
        unsupportedOperationMap = populateUnsupportedOperationMap();
    }

    @Override
    public void handleUpstream(final ChannelHandlerContext channelHandlerContext, final ChannelEvent channelEvent)
            throws Exception {
        if (Logs.isExtrrreeeme()) {
            Logs.extreme().trace(LogUtil.dumpObject(channelEvent));
        }
        if (channelEvent instanceof MessageEvent) {
            final MessageEvent msgEvent = (MessageEvent) channelEvent;
            try {
                this.incomingMessage(channelHandlerContext, msgEvent);
            } catch (Exception e) {
                Logs.extreme().trace("Error binding request", e);
                Channels.fireExceptionCaught(channelHandlerContext, e);
                return;
            }
        }
        channelHandlerContext.sendUpstream(channelEvent);
    }

    @Override
    public void incomingMessage(ChannelHandlerContext ctx, MessageEvent event) throws Exception {
        if (event.getMessage() instanceof MappingHttpRequest) {
            MappingHttpRequest httpRequest = (MappingHttpRequest) event.getMessage();
            // TODO: get real user data here too
            //Auth is already done before binding, so binding here is just a validation. Then send 100-continue
            BaseMessage msg = (BaseMessage) this.bind(httpRequest);
            httpRequest.setMessage(msg);

            if (msg instanceof ObjectStorageDataGetRequestType) {
                ObjectStorageDataGetRequestType getObject = (ObjectStorageDataGetRequestType) msg;
                getObject.setChannel(ctx.getChannel());
            }
            if (msg instanceof ObjectStorageDataRequestType) {
                String expect = httpRequest.getHeader(HttpHeaders.Names.EXPECT);
                if (expect != null) {
                    if (expect.toLowerCase().equals("100-continue")) {
                        ObjectStorageDataRequestType request = (ObjectStorageDataRequestType) msg;
                        request.setExpectHeader(true);
                    }
                }

                //handle the content.
                ObjectStorageDataRequestType request = (ObjectStorageDataRequestType) msg;
                request.setIsChunked(httpRequest.isChunked());
                handleData(request, httpRequest.getContent());
            }
        }
    }

    @Override
    public void outgoingMessage(ChannelHandlerContext ctx, MessageEvent event) throws Exception {
        if (event.getMessage() instanceof MappingHttpResponse) {
            MappingHttpResponse httpResponse = (MappingHttpResponse) event.getMessage();
            BaseMessage msg = (BaseMessage) httpResponse.getMessage();
            Binding binding;

            if (!(msg instanceof EucalyptusErrorMessageType) && !(msg instanceof ExceptionResponseType)) {
                binding = BindingManager.getBinding(super.getNamespace());
            } else {
                binding = BindingManager.getDefaultBinding();
            }
            if (msg != null) {
                ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
                byteOut.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>".getBytes("UTF-8"));
                binding.toStream(byteOut, msg);
                byte[] req = byteOut.toByteArray();
                ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(req);
                httpResponse.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(buffer.readableBytes()));
                httpResponse.setHeader(HttpHeaders.Names.CONTENT_TYPE, "application/xml");
                httpResponse.setHeader(HttpHeaders.Names.DATE,
                        DateFormatter.dateToHeaderFormattedString(new Date()));
                httpResponse.setHeader("x-amz-request-id", msg.getCorrelationId());
                httpResponse.setContent(buffer);
            }
        }
    }

    public void handleData(ObjectStorageDataRequestType dataRequest, ChannelBuffer content) {
        ChannelBufferStreamingInputStream stream = new ChannelBufferStreamingInputStream(content);
        dataRequest.setData(stream);
    }

    protected abstract Map<String, String> populateOperationMap();

    protected abstract Map<String, String> populateUnsupportedOperationMap();

    @Override
    public Object bind(final MappingHttpRequest httpRequest) throws Exception {
        Map bindingArguments = new HashMap();
        final String operationName = getOperation(httpRequest, bindingArguments);
        if (operationName == null)
            throw new MethodNotAllowedException(httpRequest.getMethod().toString() + " " + httpRequest.getUri());

        Map<String, String> params = httpRequest.getParameters();

        OMElement msg;

        GroovyObject groovyMsg;
        Map<String, String> fieldMap;
        Class targetType;
        try {
            //:: try to create the target class :://
            targetType = ClassLoader.getSystemClassLoader()
                    .loadClass("com.eucalyptus.objectstorage.msgs.".concat(operationName).concat("Type"));
            if (!GroovyObject.class.isAssignableFrom(targetType)) {
                throw new Exception();
            }
            //:: get the map of parameters to fields :://
            fieldMap = this.buildFieldMap(targetType);
            //:: get an instance of the message :://
            groovyMsg = (GroovyObject) targetType.newInstance();
        } catch (Exception e) {
            throw new BindingException("Failed to construct message of type " + operationName);
        }

        addLogData((BaseMessage) groovyMsg, bindingArguments);

        //TODO: Refactor this to be more general
        List<String> failedMappings = populateObject(groovyMsg, fieldMap, params);
        populateObjectFromBindingMap(groovyMsg, fieldMap, httpRequest, bindingArguments);

        User user = Contexts.lookup(httpRequest.getCorrelationId()).getUser();
        setRequiredParams(groovyMsg, user);

        if (!params.isEmpty()) {
            //ignore params that are not consumed, EUCA-4840
            params.clear();
        }

        if (!failedMappings.isEmpty()) {
            StringBuilder errMsg = new StringBuilder("Failed to bind the following fields:\n");
            for (String f : failedMappings)
                errMsg.append(f).append('\n');
            for (Map.Entry<String, String> f : params.entrySet())
                errMsg.append(f.getKey()).append(" = ").append(f.getValue()).append('\n');
            throw new BindingException(errMsg.toString());
        }

        Logs.extreme().trace(groovyMsg.toString());
        try {
            Binding binding = BindingManager.getDefaultBinding();
            msg = binding.toOM(groovyMsg);
        } catch (RuntimeException e) {
            throw new BindingException("Failed to build a valid message: " + e.getMessage());
        }

        return groovyMsg;

    }

    protected void addLogData(BaseMessage eucaMsg, Map bindingArguments) {
        if (eucaMsg instanceof ObjectStorageRequestType) {
            String operation = (String) bindingArguments.remove("Operation");
            if (operation != null) {
                ObjectStorageRequestType request = (ObjectStorageRequestType) eucaMsg;
                BucketLogData logData = ObjectStorageBucketLogger.getInstance()
                        .makeLogEntry(UUID.randomUUID().toString());
                logData.setOperation("REST." + operation);
                request.setLogData(logData);
            }
        }
    }

    protected void setRequiredParams(final GroovyObject msg, User user) throws Exception {
        msg.setProperty("timeStamp", new Date());
    }

    protected String getOperation(MappingHttpRequest httpRequest, Map operationParams) throws Exception {
        String[] target = null;
        String path = getOperationPath(httpRequest);
        boolean objectstorageInternalOperation = false;

        String targetHost = httpRequest.getHeader(HttpHeaders.Names.HOST);
        if (targetHost.contains(".objectstorage")) {
            String bucket = targetHost.substring(0, targetHost.indexOf(".objectstorage"));
            path = "/" + bucket + path;
        } else if (targetHost.contains(".walrus")) {
            String bucket = targetHost.substring(0, targetHost.indexOf(".walrus"));
            path = "/" + bucket + path;
        }

        if (path.length() > 0) {
            target = OSGUtil.getTarget(path);
        }

        String verb = httpRequest.getMethod().getName();
        String operationKey = "";
        Map<String, String> params = httpRequest.getParameters();
        String operationName = null;
        long contentLength = 0;
        String contentLengthString = httpRequest.getHeader(HttpHeaders.Names.CONTENT_LENGTH);
        if (contentLengthString != null) {
            contentLength = Long.parseLong(contentLengthString);
        }

        if (target == null) {
            //target = service
            operationKey = SERVICE + verb;
        } else if (target.length < 2) {
            //target = bucket
            if (!target[0].equals("")) {
                operationKey = BUCKET + verb;
                operationParams.put("Bucket", target[0]);
                operationParams.put("Operation", verb.toUpperCase() + "." + "BUCKET");
                if (verb.equals(ObjectStorageProperties.HTTPVerb.POST.toString())) {
                    //Handle POST form upload.
                    /*
                    For multipart-form uploads we get the metadata from the from fields
                    and the first data chunk from the "file" form field
                     */
                    Map formFields = httpRequest.getFormFields();
                    String objectKey = null;
                    String file = (String) formFields.get(ObjectStorageProperties.FormField.file.toString());
                    if (formFields.containsKey(ObjectStorageProperties.FormField.key.toString())) {
                        objectKey = (String) formFields.get(ObjectStorageProperties.FormField.key.toString());
                        objectKey = objectKey.replaceAll("\\$\\{filename\\}", file);
                        operationParams.put("Key", objectKey);
                    }
                    if (formFields.containsKey(ObjectStorageProperties.FormField.acl.toString())) {
                        String acl = (String) formFields.get(ObjectStorageProperties.FormField.acl.toString());
                        httpRequest.addHeader(ObjectStorageProperties.AMZ_ACL, acl);
                    }
                    if (formFields.containsKey(ObjectStorageProperties.FormField.redirect.toString())) {
                        String successActionRedirect = (String) formFields
                                .get(ObjectStorageProperties.FormField.redirect.toString());
                        operationParams.put("SuccessActionRedirect", successActionRedirect);
                    }
                    if (formFields
                            .containsKey(ObjectStorageProperties.FormField.success_action_redirect.toString())) {
                        String successActionRedirect = (String) formFields
                                .get(ObjectStorageProperties.FormField.success_action_redirect.toString());
                        operationParams.put("SuccessActionRedirect", successActionRedirect);
                    }
                    if (formFields
                            .containsKey(ObjectStorageProperties.FormField.success_action_status.toString())) {
                        Integer successActionStatus = Integer.parseInt((String) formFields
                                .get(ObjectStorageProperties.FormField.success_action_status.toString()));
                        if (successActionStatus == 200 || successActionStatus == 201) {
                            operationParams.put("SuccessActionStatus", successActionStatus);
                        } else {
                            //Default is 204, as per S3 spec
                            operationParams.put("SuccessActionStatus", 204);
                        }
                    } else {
                        operationParams.put("SuccessActionStatus", 204);
                    }

                    //Get the content-type of the upload content, not the form itself
                    if (formFields.get(ObjectStorageProperties.FormField.Content_Type.toString()) != null) {
                        operationParams.put("ContentType",
                                formFields.get(ObjectStorageProperties.FormField.Content_Type.toString()));
                    }

                    if (formFields
                            .get(ObjectStorageProperties.FormField.x_ignore_filecontentlength.toString()) != null) {
                        operationParams.put("ContentLength", (long) formFields
                                .get(ObjectStorageProperties.FormField.x_ignore_filecontentlength.toString()));
                    } else {
                        throw new MalformedPOSTRequestException(
                                "Could not calculate upload content length from request");
                        //if(contentLengthString != null) {
                        //    operationParams.put("ContentLength", (new Long(contentLength).toString()));
                        //}
                    }
                    key = target[0] + "." + objectKey;

                    //Verify the message content is found.
                    ChannelBuffer buffer = (ChannelBuffer) formFields
                            .get(ObjectStorageProperties.FormField.x_ignore_firstdatachunk.toString());
                    if (buffer == null) {
                        //No content found.
                        throw new MalformedPOSTRequestException("No upload content found");
                    }
                } else if (ObjectStorageProperties.HTTPVerb.PUT.toString().equals(verb)) {
                    if (params.containsKey(ObjectStorageProperties.BucketParameter.logging.toString())) {
                        //read logging params
                        getTargetBucketParams(operationParams, httpRequest);
                    } else if (params.containsKey(ObjectStorageProperties.BucketParameter.versioning.toString())) {
                        getVersioningStatus(operationParams, httpRequest);
                    }
                }
            } else {
                operationKey = SERVICE + verb;
            }
        } else {
            //target = object
            operationKey = OBJECT + verb;
            String objectKey = "";
            String splitOn = "";
            for (int i = 1; i < target.length; ++i) {
                objectKey += splitOn + target[i];
                splitOn = "/";
            }

            try {
                objectKey = OSGUtil.URLdecode(objectKey);
            } catch (UnsupportedEncodingException e) {
                throw new BindingException("Unable to get key: " + e.getMessage());
            }

            operationParams.put("Bucket", target[0]);
            operationParams.put("Key", objectKey);
            operationParams.put("Operation", verb.toUpperCase() + "." + "OBJECT");

            if (!params.containsKey(ObjectStorageProperties.BucketParameter.acl.toString())) {
                if (verb.equals(ObjectStorageProperties.HTTPVerb.PUT.toString())) {
                    if (httpRequest.containsHeader(ObjectStorageProperties.COPY_SOURCE.toString())) {
                        String copySource = httpRequest.getHeader(ObjectStorageProperties.COPY_SOURCE.toString());
                        try {
                            copySource = OSGUtil.URLdecode(copySource);
                        } catch (UnsupportedEncodingException ex) {
                            throw new BindingException("Unable to decode copy source: " + copySource);
                        }
                        String[] sourceParts = copySource.split("\\?");
                        if (sourceParts.length > 1) {
                            operationParams.put("SourceVersionId",
                                    sourceParts[1].replaceFirst("versionId=", "").trim());
                        }
                        copySource = sourceParts[0];
                        String[] sourceTarget = OSGUtil.getTarget(copySource);
                        String sourceObjectKey = "";
                        String sourceSplitOn = "";
                        if (sourceTarget.length > 1) {
                            for (int i = 1; i < sourceTarget.length; ++i) {
                                sourceObjectKey += sourceSplitOn + sourceTarget[i];
                                sourceSplitOn = "/";
                            }

                            operationParams.put("SourceBucket", sourceTarget[0]);
                            operationParams.put("SourceObject", sourceObjectKey);
                            operationParams.put("DestinationBucket", operationParams.remove("Bucket"));
                            operationParams.put("DestinationObject", operationParams.remove("Key"));

                            String metaDataDirective = httpRequest
                                    .getHeader(ObjectStorageProperties.METADATA_DIRECTIVE.toString());
                            if (metaDataDirective != null) {
                                operationParams.put("MetadataDirective", metaDataDirective);
                            }

                            AccessControlList accessControlList = null;
                            if (contentLength > 0) {
                                accessControlList = getAccessControlList(httpRequest);
                            } else {
                                accessControlList = new AccessControlList();
                            }
                            String aclHeader = httpRequest.getHeader(ObjectStorageProperties.AMZ_ACL);
                            if (aclHeader != null && !"".equals(aclHeader)) {
                                validateCannedAcl(aclHeader);
                                CanonicalUser aws = new CanonicalUser();
                                aws.setDisplayName("");
                                Grant grant = new Grant(new Grantee(aws), aclHeader);
                                ArrayList<Grant> grants = Lists.newArrayList();
                                grants.add(grant);
                                accessControlList.getGrants().addAll(grants);
                            }

                            operationParams.put("AccessControlList", accessControlList);
                            operationKey += ObjectStorageProperties.COPY_SOURCE.toString();
                            Set<String> headerNames = httpRequest.getHeaderNames();
                            for (String key : headerNames) {
                                if (key.startsWith("x-amz-")) {
                                    String stripped = key.replaceFirst("x-amz-", "");
                                    for (ObjectStorageProperties.CopyHeaders header : ObjectStorageProperties.CopyHeaders
                                            .values()) {
                                        if (stripped.replaceAll("-", "").equals(header.toString().toLowerCase())) {
                                            String value = httpRequest.getHeader(key);
                                            parseExtendedHeaders(operationParams, header.toString(), value);
                                        }
                                    }
                                }
                            }
                        } else {
                            throw new BindingException("Malformed COPY request");
                        }

                    } else {
                        //handle PUTs
                        key = target[0] + "." + objectKey;
                        String contentType = httpRequest.getHeader(HttpHeaders.Names.CONTENT_TYPE);
                        if (contentType != null)
                            operationParams.put("ContentType", contentType);
                        String contentDisposition = httpRequest.getHeader("Content-Disposition");
                        if (contentDisposition != null)
                            operationParams.put("ContentDisposition", contentDisposition);
                        String contentMD5 = httpRequest.getHeader(HttpHeaders.Names.CONTENT_MD5);
                        if (contentMD5 != null)
                            operationParams.put("ContentMD5", contentMD5);
                        if (contentLengthString != null)
                            operationParams.put("ContentLength", (new Long(contentLength).toString()));
                    }
                } else if (verb.equals(ObjectStorageProperties.HTTPVerb.GET.toString())) {
                    if (!objectstorageInternalOperation) {

                        if (params.containsKey("torrent")) {
                            operationParams.put("GetTorrent", Boolean.TRUE);
                        } else {
                            if (!params.containsKey("uploadId")) {
                                operationParams.put("InlineData", Boolean.FALSE);
                                operationParams.put("GetMetaData", Boolean.TRUE);
                            }
                        }

                        Set<String> headerNames = httpRequest.getHeaderNames();
                        boolean isExtendedGet = false;
                        for (String key : headerNames) {
                            for (ObjectStorageProperties.ExtendedGetHeaders header : ObjectStorageProperties.ExtendedGetHeaders
                                    .values()) {
                                if (key.replaceAll("-", "").equals(header.toString())) {
                                    String value = httpRequest.getHeader(key);
                                    isExtendedGet = true;
                                    parseExtendedHeaders(operationParams, header.toString(), value);
                                }
                            }

                        }
                        if (isExtendedGet) {
                            operationKey += "extended";
                            //only supported through SOAP
                            operationParams.put("ReturnCompleteObjectOnConditionFailure", Boolean.FALSE);
                        }
                    }
                    if (params.containsKey(ObjectStorageProperties.GetOptionalParameters.IsCompressed.toString())) {
                        Boolean isCompressed = Boolean.parseBoolean(params
                                .remove(ObjectStorageProperties.GetOptionalParameters.IsCompressed.toString()));
                        operationParams.put("IsCompressed", isCompressed);
                    }

                } else if (verb.equals(ObjectStorageProperties.HTTPVerb.POST.toString())) {
                    if (params.containsKey("uploadId")) {
                        operationParams.put("Parts", getPartsList(httpRequest));
                    }
                }
            }
            if (params.containsKey(ObjectStorageProperties.ObjectParameter.versionId.toString())) {
                if (!verb.equals(ObjectStorageProperties.HTTPVerb.DELETE.toString()))
                    operationParams.put("VersionId",
                            params.remove(ObjectStorageProperties.ObjectParameter.versionId.toString()));
            }
        }

        if (verb.equals(ObjectStorageProperties.HTTPVerb.PUT.toString())
                && params.containsKey(ObjectStorageProperties.BucketParameter.acl.toString())) {
            operationParams.put("AccessControlPolicy", getAccessControlPolicy(httpRequest));
        }

        if (verb.equals(ObjectStorageProperties.HTTPVerb.PUT.toString())
                && params.containsKey(ObjectStorageProperties.BucketParameter.lifecycle.toString())) {
            operationParams.put("lifecycleConfiguration", getLifecycle(httpRequest));
        }

        ArrayList paramsToRemove = new ArrayList();

        boolean addMore = true;
        Iterator iterator = params.keySet().iterator();
        while (iterator.hasNext()) {
            Object key = iterator.next();
            String keyString = key.toString();
            boolean dontIncludeParam = false;
            for (ObjectStorageAuthenticationHandler.SecurityParameter securityParam : ObjectStorageAuthenticationHandler.SecurityParameter
                    .values()) {
                if (keyString.equals(securityParam.toString().toLowerCase())) {
                    dontIncludeParam = true;
                    break;
                }
            }
            if (!dontIncludeParam) {
                String value = params.get(key);
                if (value != null) {
                    String[] keyStringParts = keyString.split("-");
                    if (keyStringParts.length > 1) {
                        keyString = "";
                        for (int i = 0; i < keyStringParts.length; ++i) {
                            keyString += toUpperFirst(keyStringParts[i]);
                        }
                    } else {
                        keyString = toUpperFirst(keyString);
                    }
                }
                dontIncludeParam = true;
                if (operationKey.startsWith(SERVICE)) {
                    for (ObjectStorageProperties.ServiceParameter param : ObjectStorageProperties.ServiceParameter
                            .values()) {
                        if (keyString.toLowerCase().equals(param.toString().toLowerCase())) {
                            dontIncludeParam = false;
                            break;
                        }
                    }
                } else if (operationKey.startsWith(BUCKET)) {
                    for (ObjectStorageProperties.BucketParameter param : ObjectStorageProperties.BucketParameter
                            .values()) {
                        if (keyString.toLowerCase().equals(param.toString().toLowerCase())) {
                            dontIncludeParam = false;
                            break;
                        }
                    }
                } else if (operationKey.startsWith(OBJECT)) {
                    for (ObjectStorageProperties.ObjectParameter param : ObjectStorageProperties.ObjectParameter
                            .values()) {
                        if (keyString.toLowerCase().equals(param.toString().toLowerCase())) {
                            dontIncludeParam = false;
                            break;
                        }
                    }
                }
                if (dontIncludeParam) {
                    paramsToRemove.add(key);
                }
            }
            if (dontIncludeParam)
                continue;
            String value = params.get(key);
            if (value != null) {
                operationParams.put(keyString, value);
            }

            //Add subresource params to the operationKey
            for (ObjectStorageProperties.SubResource subResource : ObjectStorageProperties.SubResource.values()) {
                if (keyString.toLowerCase().equals(subResource.toString().toLowerCase())) {
                    operationKey += keyString.toLowerCase();
                }
            }

            /*if(addMore) {
               //just add the first one to the key
               operationKey += keyString.toLowerCase();
               addMore = false;
            }*/

            paramsToRemove.add(key);
        }

        for (Object key : paramsToRemove) {
            params.remove(key);
        }

        if (!objectstorageInternalOperation) {
            operationName = operationMap.get(operationKey);
        }

        if (operationName == null) {
            String unsupportedOp = unsupportedOperationMap.get(operationKey);
            if (unsupportedOp != null) {
                String resourceType = null;
                String resource = null;
                if (target.length < 2) {
                    resourceType = BUCKET;
                    resource = target[0];
                } else {
                    resourceType = OBJECT;
                    String delimiter = new String();
                    for (int i = 1; i < target.length; ++i) {
                        resource += delimiter + target[i];
                        delimiter = "/";
                    }
                }
                NotImplementedException e = new NotImplementedException();
                e.setResource(resource);
                e.setResourceType(resourceType);
                e.setMessage(unsupportedOp + " is not implemented");
                throw e;
            }
        }

        if ("CreateBucket".equals(operationName)) {
            String locationConstraint = getLocationConstraint(httpRequest);
            if (locationConstraint != null)
                operationParams.put("LocationConstraint", locationConstraint);
        }
        return operationName;
    }

    protected void getTargetBucketParams(Map operationParams, MappingHttpRequest httpRequest)
            throws S3Exception, BindingException {
        String message = getMessageString(httpRequest);
        if (message.length() > 0) {
            try {
                XMLParser xmlParser = new XMLParser(message);
                String targetBucket = xmlParser.getValue("//TargetBucket");
                if (targetBucket == null || targetBucket.length() == 0)
                    return;
                String targetPrefix = xmlParser.getValue("//TargetPrefix");
                ArrayList<Grant> grants = new ArrayList<Grant>();

                List<String> permissions = xmlParser.getValues("//TargetGrants/Grant/Permission");
                if (permissions == null)
                    throw new MalformedACLErrorException("/TargetGrants/Grant/Permission");

                DTMNodeList grantees = xmlParser.getNodes("//TargetGrants/Grant/Grantee");
                if (grantees == null)
                    throw new MalformedACLErrorException("//TargetGrants/Grant/Grantee");

                for (int i = 0; i < grantees.getLength(); ++i) {
                    String id = xmlParser.getValue(grantees.item(i), "ID");
                    if (id.length() > 0) {
                        String canonicalUserName = xmlParser.getValue(grantees.item(i), "DisplayName");
                        Grant grant = new Grant();
                        Grantee grantee = new Grantee();
                        grantee.setCanonicalUser(new CanonicalUser(id, canonicalUserName));
                        grant.setGrantee(grantee);
                        grant.setPermission(permissions.get(i));
                        grants.add(grant);
                    } else {
                        String groupUri = xmlParser.getValue(grantees.item(i), "URI");
                        if (groupUri.length() == 0)
                            throw new MalformedACLErrorException("ACL group URI");
                        Grant grant = new Grant();
                        Grantee grantee = new Grantee();
                        grantee.setGroup(new Group(groupUri));
                        grant.setGrantee(grantee);
                        grant.setPermission(permissions.get(i));
                        grants.add(grant);
                    }
                }
                TargetGrants targetGrants = new TargetGrants(grants);
                LoggingEnabled loggingEnabled = new LoggingEnabled(targetBucket, targetPrefix,
                        new TargetGrants(grants));
                operationParams.put("LoggingEnabled", loggingEnabled);
            } catch (S3Exception e) {
                throw e;
            } catch (Exception ex) {
                MalformedXMLException malEx = new MalformedXMLException("//TargetBucket");
                malEx.initCause(ex);
                throw malEx;
            }
        }
    }

    protected void getVersioningStatus(Map operationParams, MappingHttpRequest httpRequest)
            throws S3Exception, BindingException {
        String message = getMessageString(httpRequest);
        if (message.length() > 0) {
            try {
                XMLParser xmlParser = new XMLParser(message);
                String status = xmlParser.getValue("//Status");
                if (status == null || status.length() == 0)
                    return;
                operationParams.put("VersioningStatus", status);
            } catch (Exception ex) {
                MalformedXMLException malEx = new MalformedXMLException(message);
                malEx.initCause(ex);
                throw malEx;
            }
        }
    }

    protected void parseExtendedHeaders(Map operationParams, String headerString, String value)
            throws BindingException {
        if (headerString.equals(ObjectStorageProperties.ExtendedGetHeaders.Range.toString())) {
            String prefix = "bytes=";
            assert (value.startsWith(prefix));
            value = value.substring(prefix.length());
            String[] values = value.split("-");
            if (values[0].equals("")) {
                operationParams.put(ObjectStorageProperties.ExtendedHeaderRangeTypes.ByteRangeStart.toString(),
                        new Long(0));
            } else {
                operationParams.put(ObjectStorageProperties.ExtendedHeaderRangeTypes.ByteRangeStart.toString(),
                        Long.parseLong(values[0]));
            }
            if ((values.length < 2) || (values[1].equals(""))) {
                //-1 is treated by the back end as end of object
                operationParams.put(ObjectStorageProperties.ExtendedHeaderRangeTypes.ByteRangeEnd.toString(),
                        new Long(-1));
            } else {
                operationParams.put(ObjectStorageProperties.ExtendedHeaderRangeTypes.ByteRangeEnd.toString(),
                        Long.parseLong(values[1]));
            }
        } else if (ObjectStorageProperties.ExtendedHeaderDateTypes.contains(headerString)) {
            try {
                List<String> dateFormats = new ArrayList<String>();
                dateFormats.add(DateUtil.PATTERN_RFC1123);
                operationParams.put(headerString, DateUtil.parseDate(value, dateFormats));
            } catch (Exception ex) {
                try {
                    operationParams.put(headerString, DateUtils.parseIso8601DateTime(value));
                } catch (ParseException e) {
                    throw new BindingException("Error parsing date: " + value, e);
                }
            }
        } else {
            operationParams.put(headerString, value);
        }
    }

    protected AccessControlPolicy getAccessControlPolicy(MappingHttpRequest httpRequest)
            throws S3Exception, BindingException {
        AccessControlPolicy accessControlPolicy = new AccessControlPolicy();
        AccessControlList accessControlList = new AccessControlList();
        ArrayList<Grant> grants = new ArrayList<Grant>();
        String aclString = "";
        try {
            aclString = getMessageString(httpRequest);
            if (aclString.length() > 0) {
                XMLParser xmlParser = new XMLParser(aclString);
                String ownerId = xmlParser.getValue("//Owner/ID");
                String displayName = xmlParser.getValue("//Owner/DisplayName");

                CanonicalUser canonicalUser = new CanonicalUser(ownerId, displayName);
                accessControlPolicy.setOwner(canonicalUser);

                List<String> permissions = xmlParser.getValues("//AccessControlList/Grant/Permission");
                if (permissions == null) {
                    throw new MalformedACLErrorException("//AccessControlList/Grant/Permission");
                }
                DTMNodeList grantees = xmlParser.getNodes("//AccessControlList/Grant/Grantee");
                if (grantees == null) {
                    throw new MalformedACLErrorException("//AccessControlList/Grant/Grantee");
                }
                for (int i = 0; i < grantees.getLength(); ++i) {
                    String id = xmlParser.getValue(grantees.item(i), "ID");
                    if (id.length() > 0) {
                        String canonicalUserName = xmlParser.getValue(grantees.item(i), "DisplayName");
                        Grant grant = new Grant();
                        Grantee grantee = new Grantee();
                        grantee.setCanonicalUser(new CanonicalUser(id, canonicalUserName));
                        grant.setGrantee(grantee);
                        grant.setPermission(permissions.get(i));
                        grants.add(grant);
                    } else if (!"".equals(xmlParser.getValue(grantees.item(i), "EmailAddress"))) {
                        String canonicalUserName = xmlParser.getValue(grantees.item(i), "DisplayName");
                        Grant grant = new Grant();
                        Grantee grantee = new Grantee();
                        String email = xmlParser.getValue(grantees.item(i), "EmailAddress");
                        grantee.setCanonicalUser(new CanonicalUser(email, canonicalUserName));
                        grant.setGrantee(grantee);
                        grant.setPermission(permissions.get(i));
                        grants.add(grant);
                    } else {
                        String groupUri = xmlParser.getValue(grantees.item(i), "URI");
                        if (groupUri.length() == 0) {
                            throw new MalformedACLErrorException("Group-URI");
                        }
                        Grant grant = new Grant();
                        Grantee grantee = new Grantee();
                        grantee.setGroup(new Group(groupUri));
                        grant.setGrantee(grantee);
                        grant.setPermission(permissions.get(i));
                        grants.add(grant);
                    }
                }
            }
        } catch (S3Exception e) {
            //pass it up
            throw e;
        } catch (Exception ex) {
            throw new MalformedACLErrorException(aclString);
        }
        accessControlList.setGrants(grants);
        accessControlPolicy.setAccessControlList(accessControlList);
        return accessControlPolicy;
    }

    protected AccessControlList getAccessControlList(MappingHttpRequest httpRequest)
            throws S3Exception, BindingException {
        AccessControlList accessControlList = new AccessControlList();
        ArrayList<Grant> grants = new ArrayList<Grant>();
        String aclString = "";
        try {
            aclString = getMessageString(httpRequest);
            if (aclString.length() > 0) {
                XMLParser xmlParser = new XMLParser(aclString);
                String ownerId = xmlParser.getValue("//Owner/ID");
                String displayName = xmlParser.getValue("//Owner/DisplayName");

                List<String> permissions = xmlParser.getValues("/AccessControlList/Grant/Permission");
                if (permissions == null) {
                    throw new MalformedACLErrorException("/AccessControlList/Grant/Permission");
                }
                DTMNodeList grantees = xmlParser.getNodes("/AccessControlList/Grant/Grantee");
                if (grantees == null) {
                    throw new MalformedACLErrorException("/AccessControlList/Grant/Grantee");
                }

                for (int i = 0; i < grantees.getLength(); ++i) {
                    String canonicalUserName = xmlParser.getValue(grantees.item(i), "DisplayName");
                    if (canonicalUserName.length() > 0) {
                        String id = xmlParser.getValue(grantees.item(i), "ID");
                        Grant grant = new Grant();
                        Grantee grantee = new Grantee();
                        grantee.setCanonicalUser(new CanonicalUser(id, canonicalUserName));
                        grant.setGrantee(grantee);
                        grant.setPermission(permissions.get(i));
                        grants.add(grant);
                    } else {
                        String groupUri = xmlParser.getValue(grantees.item(i), "URI");
                        if (groupUri.length() == 0)
                            throw new MalformedACLErrorException("Group-URI");
                        Grant grant = new Grant();
                        Grantee grantee = new Grantee();
                        grantee.setGroup(new Group(groupUri));
                        grant.setGrantee(grantee);
                        grant.setPermission(permissions.get(i));
                        grants.add(grant);
                    }
                }
            }
        } catch (S3Exception e) {
            throw e;
        } catch (Exception ex) {
            throw new MalformedACLErrorException("Unable to parse access control list: " + aclString);
        }
        accessControlList.setGrants(grants);
        return accessControlList;
    }

    private ArrayList<Part> getPartsList(MappingHttpRequest httpRequest) throws BindingException {
        ArrayList<Part> partList = new ArrayList<Part>();
        try {
            String partsString = getMessageString(httpRequest);
            if (partsString.length() > 0) {
                XMLParser xmlParser = new XMLParser(partsString);
                DTMNodeList partNodes = xmlParser.getNodes("/CompleteMultipartUpload/Part");
                if (partNodes == null) {
                    throw new BindingException("Malformed part list");
                }

                for (int i = 0; i < partNodes.getLength(); i++) {
                    Part part = new Part(Integer.parseInt(xmlParser.getValue(partNodes.item(i), "PartNumber")),
                            xmlParser.getValue(partNodes.item(i), "ETag"));
                    partList.add(part);
                }
            }
        } catch (Exception ex) {
            throw new BindingException("Unable to parse part list " + ex.getMessage());
        }
        return partList;
    }

    /**
     * Removes the service path for processing the bucket/key split.
     * @param httpRequest
     * @return
     */
    protected String getOperationPath(MappingHttpRequest httpRequest) {
        for (String pathCandidate : ObjectStorageRESTPipeline.getServicePaths()) {
            if (httpRequest.getServicePath().startsWith(pathCandidate)) {
                String opPath = httpRequest.getServicePath().replaceFirst(pathCandidate, "");
                if (!Strings.isNullOrEmpty(opPath) && !opPath.startsWith("/")) {
                    //The service path was not demarked with a /, e.g. /services/objectstorageblahblah -> blahblah
                    //So, don't remove the service path because that changes the semantics.
                    break;
                } else {
                    return opPath;
                }
            }
        }

        return httpRequest.getServicePath();
    }

    protected String getLocationConstraint(MappingHttpRequest httpRequest) throws S3Exception {
        String locationConstraint = null;
        try {
            String bucketConfiguration = getMessageString(httpRequest);
            if (bucketConfiguration.length() > 0) {
                XMLParser xmlParser = new XMLParser(bucketConfiguration);
                locationConstraint = xmlParser.getValue("/CreateBucketConfiguration/LocationConstraint");
            }
        } catch (Exception ex) {
            MalformedXMLException malEx = new MalformedXMLException(
                    "/CreateBucketConfiguration/LocationConstraint");
            malEx.initCause(ex);
            throw malEx;

        }
        return locationConstraint;
    }

    protected List<String> populateObject(final GroovyObject obj, final Map<String, String> paramFieldMap,
            final Map<String, String> params) {
        List<String> failedMappings = new ArrayList<String>();
        for (Map.Entry<String, String> e : paramFieldMap.entrySet()) {
            try {
                if (obj.getClass().getDeclaredField(e.getValue()).getType().equals(ArrayList.class))
                    failedMappings.addAll(populateObjectList(obj, e, params, params.size()));
            } catch (NoSuchFieldException e1) {
                failedMappings.add(e.getKey());
            }
        }
        for (Map.Entry<String, String> e : paramFieldMap.entrySet()) {
            if (params.containsKey(e.getKey()) && !populateObjectField(obj, e, params))
                failedMappings.add(e.getKey());
        }
        return failedMappings;
    }

    protected void populateObjectFromBindingMap(final GroovyObject obj, final Map<String, String> paramFieldMap,
            final MappingHttpRequest httpRequest, final Map bindingMap) throws S3Exception, BindingException {
        //process headers
        String aclString = httpRequest.getAndRemoveHeader(ObjectStorageProperties.AMZ_ACL);
        if (aclString != null) {
            addAccessControlList(obj, paramFieldMap, bindingMap, aclString);
        }

        //add meta data
        String metaDataString = paramFieldMap.remove("MetaData");
        if (metaDataString != null) {
            Set<String> headerNames = httpRequest.getHeaderNames();
            ArrayList<MetaDataEntry> metaData = new ArrayList<MetaDataEntry>();
            for (String key : headerNames) {
                if (key.toLowerCase().startsWith(ObjectStorageProperties.AMZ_META_HEADER_PREFIX)) {
                    MetaDataEntry metaDataEntry = new MetaDataEntry();
                    metaDataEntry.setName(key.substring(ObjectStorageProperties.AMZ_META_HEADER_PREFIX.length()));
                    metaDataEntry.setValue(httpRequest.getHeader(key));
                    metaData.add(metaDataEntry);
                }
            }
            obj.setProperty(metaDataString, metaData);
        }

        //populate from binding map (required params)
        Iterator bindingMapIterator = bindingMap.keySet().iterator();
        while (bindingMapIterator.hasNext()) {
            String key = (String) bindingMapIterator.next();
            obj.setProperty(key.substring(0, 1).toLowerCase().concat(key.substring(1)), bindingMap.get(key));
        }
    }

    protected boolean populateObjectField(final GroovyObject obj, final Map.Entry<String, String> paramFieldPair,
            final Map<String, String> params) {
        try {
            Class declaredType = obj.getClass().getDeclaredField(paramFieldPair.getValue()).getType();
            if (declaredType.equals(String.class))
                obj.setProperty(paramFieldPair.getValue(), params.remove(paramFieldPair.getKey()));
            else if (declaredType.getName().equals("int"))
                obj.setProperty(paramFieldPair.getValue(),
                        Integer.parseInt(params.remove(paramFieldPair.getKey())));
            else if (declaredType.equals(Integer.class))
                obj.setProperty(paramFieldPair.getValue(), new Integer(params.remove(paramFieldPair.getKey())));
            else if (declaredType.getName().equals("boolean"))
                obj.setProperty(paramFieldPair.getValue(),
                        Boolean.parseBoolean(params.remove(paramFieldPair.getKey())));
            else if (declaredType.equals(Boolean.class))
                obj.setProperty(paramFieldPair.getValue(), new Boolean(params.remove(paramFieldPair.getKey())));
            else if (declaredType.getName().equals("long"))
                obj.setProperty(paramFieldPair.getValue(), Long.parseLong(params.remove(paramFieldPair.getKey())));
            else if (declaredType.equals(Long.class))
                obj.setProperty(paramFieldPair.getValue(), new Long(params.remove(paramFieldPair.getKey())));
            else
                return false;
            return true;
        } catch (Exception e1) {
            return false;
        }
    }

    protected List<String> populateObjectList(final GroovyObject obj,
            final Map.Entry<String, String> paramFieldPair, final Map<String, String> params, final int paramSize) {
        List<String> failedMappings = new ArrayList<String>();
        try {
            Field declaredField = obj.getClass().getDeclaredField(paramFieldPair.getValue());
            ArrayList theList = (ArrayList) obj.getProperty(paramFieldPair.getValue());
            Class genericType = (Class) ((ParameterizedType) declaredField.getGenericType())
                    .getActualTypeArguments()[0];
            // :: simple case: FieldName.# :://
            if (String.class.equals(genericType)) {
                if (params.containsKey(paramFieldPair.getKey())) {
                    theList.add(params.remove(paramFieldPair.getKey()));
                } else {
                    List<String> keys = Lists.newArrayList(params.keySet());
                    for (String k : keys) {
                        if (k.matches(paramFieldPair.getKey() + "\\.\\d*")) {
                            theList.add(params.remove(k));
                        }
                    }
                }
            } else if (declaredField.isAnnotationPresent(HttpEmbedded.class)) {
                HttpEmbedded annoteEmbedded = (HttpEmbedded) declaredField.getAnnotation(HttpEmbedded.class);
                // :: build the parameter map and call populate object recursively :://
                if (annoteEmbedded.multiple()) {
                    String prefix = paramFieldPair.getKey();
                    List<String> embeddedListFieldNames = new ArrayList<String>();
                    for (String actualParameterName : params.keySet())
                        if (actualParameterName.matches(prefix + ".1.*"))
                            embeddedListFieldNames.add(actualParameterName.replaceAll(prefix + ".1.", ""));
                    for (int i = 0; i < paramSize + 1; i++) {
                        boolean foundAll = true;
                        Map<String, String> embeddedParams = new HashMap<String, String>();
                        for (String fieldName : embeddedListFieldNames) {
                            String paramName = prefix + "." + i + "." + fieldName;
                            if (!params.containsKey(paramName)) {
                                failedMappings.add("Mismatched embedded field: " + paramName);
                                foundAll = false;
                            } else
                                embeddedParams.put(fieldName, params.get(paramName));
                        }
                        if (foundAll)
                            failedMappings.addAll(populateEmbedded(genericType, embeddedParams, theList));
                        else
                            break;
                    }
                } else
                    failedMappings.addAll(populateEmbedded(genericType, params, theList));
            }
        } catch (Exception e1) {
            failedMappings.add(paramFieldPair.getKey());
        }
        return failedMappings;
    }

    protected List<String> populateEmbedded(final Class genericType, final Map<String, String> params,
            final ArrayList theList) throws InstantiationException, IllegalAccessException {
        GroovyObject embedded = (GroovyObject) genericType.newInstance();
        Map<String, String> embeddedFields = buildFieldMap(genericType);
        int startSize = params.size();
        List<String> embeddedFailures = populateObject(embedded, embeddedFields, params);
        if (embeddedFailures.isEmpty() && !(params.size() - startSize == 0))
            theList.add(embedded);
        return embeddedFailures;
    }

    protected Map<String, String> buildFieldMap(final Class targetType) {
        Map<String, String> fieldMap = new HashMap<String, String>();
        Field[] fields = targetType.getDeclaredFields();
        for (Field f : fields)
            if (Modifier.isStatic(f.getModifiers()))
                continue;
            else if (f.isAnnotationPresent(HttpParameterMapping.class)) {
                for (final String parameter : f.getAnnotation(HttpParameterMapping.class).parameter())
                    fieldMap.put(parameter, f.getName());
                fieldMap.put(f.getName().substring(0, 1).toUpperCase().concat(f.getName().substring(1)),
                        f.getName());
            } else
                fieldMap.put(f.getName().substring(0, 1).toUpperCase().concat(f.getName().substring(1)),
                        f.getName());
        return fieldMap;
    }

    protected static void validateCannedAcl(String cannedAcl) throws InvalidArgumentException {
        if (!AclUtils.isValidCannedAcl(cannedAcl)) {
            throw new InvalidArgumentException(cannedAcl);
        }
    }

    protected static void addAccessControlList(final GroovyObject obj, final Map<String, String> paramFieldMap,
            Map bindingMap, String cannedACLString) throws S3Exception {

        AccessControlList accessControlList;
        ArrayList<Grant> grants;

        if (bindingMap.containsKey("AccessControlPolicy")) {
            AccessControlPolicy accessControlPolicy = (AccessControlPolicy) bindingMap.get("AccessControlPolicy");
            accessControlList = accessControlPolicy.getAccessControlList();
            grants = accessControlList.getGrants();
        } else {
            accessControlList = new AccessControlList();
            grants = new ArrayList<Grant>();
        }

        validateCannedAcl(cannedACLString);

        CanonicalUser aws = new CanonicalUser();
        aws.setDisplayName("");
        Grant grant = new Grant(new Grantee(aws), cannedACLString);
        grants.add(grant);

        accessControlList.setGrants(grants);
        //set obj property
        String acl = paramFieldMap.remove("AccessControlList");
        if (acl != null) {
            obj.setProperty(acl, accessControlList);
        }
    }

    protected String toUpperFirst(String string) {
        return string.substring(0, 1).toUpperCase().concat(string.substring(1));
    }

    protected String getMessageString(MappingHttpRequest httpRequest) {
        ChannelBuffer buffer = httpRequest.getContent();
        buffer.markReaderIndex();
        byte[] read = new byte[buffer.readableBytes()];
        buffer.readBytes(read);
        return new String(read);
    }

    private LifecycleConfiguration getLifecycle(MappingHttpRequest httpRequest) throws S3Exception {
        LifecycleConfiguration lifecycleConfigurationType = new LifecycleConfiguration();
        lifecycleConfigurationType.setRules(new ArrayList<LifecycleRule>());
        String message = getMessageString(httpRequest);
        if (message.length() > 0) {
            try {
                XMLParser xmlParser = new XMLParser(message);
                DTMNodeList rules = xmlParser.getNodes("//LifecycleConfiguration/Rule");
                if (rules == null) {
                    throw new MalformedXMLException("/LifecycleConfiguration/Rule");
                }
                for (int idx = 0; idx < rules.getLength(); idx++) {
                    lifecycleConfigurationType.getRules().add(extractLifecycleRule(xmlParser, rules.item(idx)));
                }
            } catch (S3Exception e) {
                throw e;
            } catch (Exception ex) {
                MalformedXMLException e = new MalformedXMLException("/LifecycleConfiguration");
                ex.initCause(ex);
                throw e;
            }
        }
        return lifecycleConfigurationType;
    }

    private LifecycleRule extractLifecycleRule(XMLParser parser, Node node) throws S3Exception {
        LifecycleRule lifecycleRule = new LifecycleRule();
        String id = parser.getValue(node, "ID");
        String prefix = parser.getValue(node, "Prefix");
        String status = parser.getValue(node, "Status");

        lifecycleRule.setId(id);
        lifecycleRule.setPrefix(prefix);
        lifecycleRule.setStatus(status);

        try {
            Transition transition = null;
            String transitionDays = parser.getValue(node, "Transition/Days");
            String transitionDate = parser.getValue(node, "Transition/Date");
            if ((transitionDays != null && !transitionDays.equals(""))
                    || (transitionDate != null && !transitionDate.equals(""))) {
                String storageClass = parser.getValue(node, "Transition/StorageClass");
                if (transitionDays != null && !transitionDays.equals("")) {
                    transition = new Transition();
                    Integer transitionDaysInt = new Integer(Integer.parseInt(transitionDays));
                    transition.setCreationDelayDays(transitionDaysInt.intValue());
                } else if (transitionDate != null && !transitionDate.equals("")) {
                    transition = new Transition();
                    Date transitionDateAsDate = Iso8601DateParser.parse(transitionDate);
                    transition.setEffectiveDate(transitionDateAsDate);
                }
                if (transition != null) {
                    transition.setDestinationClass(storageClass);
                }
            }
            if (transition != null) {
                lifecycleRule.setTransition(transition);
            }

            Expiration expiration = null;
            String expirationDays = parser.getValue(node, "Expiration/Days");
            String expirationDate = parser.getValue(node, "Expiration/Date");
            if ((expirationDays != null && !expirationDays.equals(""))
                    || (expirationDate != null && !expirationDate.equals(""))) {
                if (expirationDays != null && !expirationDays.equals("")) {
                    expiration = new Expiration();
                    Integer expirationDaysInt = new Integer(Integer.parseInt(expirationDays));
                    expiration.setCreationDelayDays(expirationDaysInt.intValue());
                } else if (expirationDate != null && !expirationDate.equals("")) {
                    expiration = new Expiration();
                    Date expirationDateAsDate = Iso8601DateParser.parse(expirationDate);
                    expiration.setEffectiveDate(expirationDateAsDate);
                }
            }
            if (expiration != null) {
                lifecycleRule.setExpiration(expiration);
            }
        } catch (ParseException e) {
            MalformedXMLException ex = new MalformedXMLException("Expiration|Transition Date");
            ex.initCause(e);
            throw ex;
        } catch (Exception ex) {
            MalformedXMLException e = new MalformedXMLException("LifecycleRule");
            ex.initCause(ex);
            throw e;
        }

        return lifecycleRule;
    }
}