/*************************************************************************
* 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.walrus.pipeline;
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;
import java.util.concurrent.TimeUnit;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.axiom.om.OMElement;
import org.apache.commons.httpclient.util.DateUtil;
import org.apache.log4j.Logger;
import com.eucalyptus.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.Channel;
import org.jboss.netty.channel.ChannelEvent;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.DownstreamMessageEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpChunk;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;
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.component.ComponentIds;
import com.eucalyptus.context.Context;
import com.eucalyptus.context.Contexts;
import com.eucalyptus.crypto.Crypto;
import com.eucalyptus.http.MappingHttpRequest;
import com.eucalyptus.http.MappingHttpResponse;
import com.eucalyptus.records.Logs;
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.Grant;
import com.eucalyptus.storage.msgs.s3.Grantee;
import com.eucalyptus.storage.msgs.s3.Group;
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.util.LogUtil;
import com.eucalyptus.util.XMLParser;
import com.eucalyptus.walrus.WalrusBackend;
import com.eucalyptus.walrus.exceptions.NotImplementedException;
import com.eucalyptus.walrus.msgs.WalrusDataGetRequestType;
import com.eucalyptus.walrus.msgs.WalrusDataMessage;
import com.eucalyptus.walrus.msgs.WalrusDataMessenger;
import com.eucalyptus.walrus.msgs.WalrusDataQueue;
import com.eucalyptus.walrus.msgs.WalrusDataRequestType;
import com.eucalyptus.walrus.msgs.WalrusRequestType;
import com.eucalyptus.walrus.util.WalrusProperties;
import com.eucalyptus.walrus.util.WalrusUtil;
import com.eucalyptus.ws.MethodNotAllowedException;
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;
public class WalrusRESTBinding extends RestfulMarshallingHandler {
private static Logger LOG = Logger.getLogger(WalrusRESTBinding.class);
private static final String SERVICE = "service";
private static final String BUCKET = "bucket";
private static final String OBJECT = "object";
private static final Map<String, String> operationMap = populateOperationMap();
private static final Map<String, String> unsupportedOperationMap = populateUnsupportedOperationMap();
private static final Map<String, String> notAllowedOperationMap = populateNotAllowedOperationMap();
private static WalrusDataMessenger putMessenger;
public static final int DATA_MESSAGE_SIZE = 102400;
private String key;
private String randomKey;
private WalrusDataQueue<WalrusDataMessage> putQueue;
private final String walrusServicePath;
public WalrusRESTBinding() {
super("http://walrus.s3.amazonaws.com/doc/" + WalrusProperties.NAMESPACE_VERSION);
walrusServicePath = ComponentIds.lookup(WalrusBackend.class).getServicePath();
}
@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 (com.eucalyptus.walrus.exceptions.MethodNotAllowedException e) {
// there are some valid cases where 405 should be thrown
Channels.fireExceptionCaught(channelHandlerContext, e);
return;
} catch (Exception e) {
LOG.warn("Error processing incoming message in Walrus Binding", e);
// throw e;
Channels.fireExceptionCaught(channelHandlerContext, e);
return;
}
} else if (channelEvent.toString().contains("DISCONNECTED") || channelEvent.toString().contains("CLOSED")) {
if (key != null && randomKey != null) {
putMessenger.removeQueue(key, randomKey);
putQueue = null;
}
}
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 WalrusDataGetRequestType) {
WalrusDataGetRequestType getObject = (WalrusDataGetRequestType) msg;
getObject.setChannel(ctx.getChannel());
}
if (msg instanceof WalrusDataRequestType) {
String expect = httpRequest.getHeader(HttpHeaders.Names.EXPECT);
if (expect != null) {
if (expect.toLowerCase().equals("100-continue")) {
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
DownstreamMessageEvent newEvent = new DownstreamMessageEvent(ctx.getChannel(), event.getFuture(), response, null);
final Channel channel = ctx.getChannel();
if (channel.isConnected()) {
ChannelFuture writeFuture = Channels.future(ctx.getChannel());
Channels.write(ctx, writeFuture, response);
}
ctx.sendDownstream(newEvent);
}
}
}
} else if (event.getMessage() instanceof HttpChunk) {
if (putQueue != null) {
HttpChunk httpChunk = (HttpChunk) event.getMessage();
handleHttpChunk(httpChunk);
}
}
}
@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());
if (putQueue != null) {
putQueue = null;
}
} else {
binding = BindingManager.getDefaultBinding();
if (putQueue != null) {
putQueue = null;
}
}
if (msg != null) {
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
binding.toStream(byteOut, msg);
byte[] req = byteOut.toByteArray();
ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(req);
httpResponse.addHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(buffer.readableBytes()));
httpResponse.addHeader(HttpHeaders.Names.CONTENT_TYPE, "application/xml");
httpResponse.setContent(buffer);
}
}
}
private static Map<String, String> populateOperationMap() {
Map<String, String> newMap = new HashMap<String, String>();
// Service operations
newMap.put(SERVICE + WalrusProperties.HTTPVerb.GET.toString(), "ListAllMyBuckets");
// Bucket operations
newMap.put(BUCKET + WalrusProperties.HTTPVerb.HEAD.toString(), "HeadBucket");
newMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.BucketParameter.acl.toString(), "GetBucketAccessControlPolicy");
newMap.put(BUCKET + WalrusProperties.HTTPVerb.PUT.toString() + WalrusProperties.BucketParameter.acl.toString(),
"SetRESTBucketAccessControlPolicy");
newMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString(), "ListBucket");
newMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.BucketParameter.prefix.toString(), "ListBucket");
newMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.BucketParameter.maxkeys.toString(), "ListBucket");
newMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.BucketParameter.marker.toString(), "ListBucket");
newMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.BucketParameter.delimiter.toString(), "ListBucket");
newMap.put(BUCKET + WalrusProperties.HTTPVerb.PUT.toString(), "CreateBucket");
newMap.put(BUCKET + WalrusProperties.HTTPVerb.DELETE.toString(), "DeleteBucket");
newMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.BucketParameter.location.toString(), "GetBucketLocation");
newMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.BucketParameter.logging.toString(), "GetBucketLoggingStatus");
newMap.put(BUCKET + WalrusProperties.HTTPVerb.PUT.toString() + WalrusProperties.BucketParameter.logging.toString(), "SetBucketLoggingStatus");
newMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.BucketParameter.versions.toString(), "ListVersions");
newMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.BucketParameter.versioning.toString(),
"GetBucketVersioningStatus");
newMap.put(BUCKET + WalrusProperties.HTTPVerb.PUT.toString() + WalrusProperties.BucketParameter.versioning.toString(),
"SetBucketVersioningStatus");
// Object operations
newMap.put(OBJECT + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.ObjectParameter.acl.toString(), "GetObjectAccessControlPolicy");
newMap.put(OBJECT + WalrusProperties.HTTPVerb.PUT.toString() + WalrusProperties.ObjectParameter.acl.toString(),
"SetRESTObjectAccessControlPolicy");
newMap.put(BUCKET + WalrusProperties.HTTPVerb.POST.toString(), "PostObject");
newMap.put(OBJECT + WalrusProperties.HTTPVerb.PUT.toString(), "PutObject");
newMap.put(OBJECT + WalrusProperties.HTTPVerb.PUT.toString() + WalrusProperties.COPY_SOURCE.toString(), "CopyObject");
newMap.put(OBJECT + WalrusProperties.HTTPVerb.GET.toString(), "GetObject");
newMap.put(OBJECT + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.ObjectParameter.torrent.toString(), "GetObject");
newMap.put(OBJECT + WalrusProperties.HTTPVerb.DELETE.toString(), "DeleteObject");
newMap.put(OBJECT + WalrusProperties.HTTPVerb.HEAD.toString(), "GetObject");
newMap.put(OBJECT + WalrusProperties.HTTPVerb.GET.toString() + "extended", "GetObjectExtended");
newMap.put(OBJECT + WalrusProperties.HTTPVerb.DELETE.toString() + WalrusProperties.ObjectParameter.versionId.toString().toLowerCase(),
"DeleteVersion");
// For multipart uploads
newMap.put(OBJECT + WalrusProperties.HTTPVerb.POST.toString() + WalrusProperties.ObjectParameter.uploads.toString(), "InitiateMultipartUpload");
newMap.put(OBJECT + WalrusProperties.HTTPVerb.PUT.toString() + WalrusProperties.ObjectParameter.partNumber.toString().toLowerCase()
+ WalrusProperties.ObjectParameter.uploadId.toString().toLowerCase(), "UploadPart");
newMap.put(OBJECT + WalrusProperties.HTTPVerb.PUT.toString() + WalrusProperties.ObjectParameter.uploadId.toString().toLowerCase()
+ WalrusProperties.ObjectParameter.partNumber.toString().toLowerCase(), "UploadPart");
newMap.put(OBJECT + WalrusProperties.HTTPVerb.POST.toString() + WalrusProperties.ObjectParameter.uploadId.toString().toLowerCase(),
"CompleteMultipartUpload");
newMap.put(OBJECT + WalrusProperties.HTTPVerb.DELETE.toString() + WalrusProperties.ObjectParameter.uploadId.toString().toLowerCase(),
"AbortMultipartUpload");
return newMap;
}
private static Map<String, String> populateNotAllowedOperationMap() {
Map<String, String> opsMap = new HashMap<String, String>();
opsMap.put(SERVICE + WalrusProperties.HTTPVerb.HEAD.toString(), "HEAD to Service");
return opsMap;
}
private static Map<String, String> populateUnsupportedOperationMap() {
Map<String, String> opsMap = new HashMap<String, String>();
// Bucket operations
// Cross-Origin Resource Sharing (cors)
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.BucketParameter.cors.toString(), "GET Bucket cors");
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.PUT.toString() + WalrusProperties.BucketParameter.cors.toString(), "PUT Bucket cors");
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.DELETE.toString() + WalrusProperties.BucketParameter.cors.toString(), "DELETE Bucket cors");
// Lifecycle
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.BucketParameter.lifecycle.toString(), "GET Bucket lifecycle");
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.PUT.toString() + WalrusProperties.BucketParameter.lifecycle.toString(), "PUT Bucket lifecycle");
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.DELETE.toString() + WalrusProperties.BucketParameter.lifecycle.toString(),
"DELETE Bucket lifecycle");
// Policy
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.BucketParameter.policy.toString(), "GET Bucket policy");
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.PUT.toString() + WalrusProperties.BucketParameter.policy.toString(), "PUT Bucket policy");
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.DELETE.toString() + WalrusProperties.BucketParameter.policy.toString(), "DELETE Bucket policy");
// Notification
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.BucketParameter.notification.toString(),
"GET Bucket notification");
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.PUT.toString() + WalrusProperties.BucketParameter.notification.toString(),
"PUT Bucket notification");
// Tagging
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.BucketParameter.tagging.toString(), "GET Bucket tagging");
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.PUT.toString() + WalrusProperties.BucketParameter.tagging.toString(), "PUT Bucket tagging");
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.DELETE.toString() + WalrusProperties.BucketParameter.tagging.toString(), "DELETE Bucket tagging");
// Request Payments // TODO HACK! binding code converts parameters to lower case. Fix that issue!
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.BucketParameter.requestPayment.toString().toLowerCase(),
"GET Bucket requestPayment");
// Website
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.BucketParameter.website.toString(), "GET Bucket website");
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.PUT.toString() + WalrusProperties.BucketParameter.website.toString(), "PUT Bucket website");
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.DELETE.toString() + WalrusProperties.BucketParameter.website.toString(), "DELETE Bucket website");
// Multipart uploads
opsMap.put(BUCKET + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.BucketParameter.uploads.toString(), "List Multipart Uploads");
// Object operations
// Multipart Uploads
opsMap.put(OBJECT + WalrusProperties.HTTPVerb.GET.toString() + WalrusProperties.ObjectParameter.uploadId.toString().toLowerCase(), "List Parts");
opsMap.put(OBJECT + WalrusProperties.HTTPVerb.PUT.toString() + WalrusProperties.ObjectParameter.uploadId.toString().toLowerCase()
+ WalrusProperties.ObjectParameter.partNumber.toString().toLowerCase(), "Upload Part");
opsMap.put(OBJECT + WalrusProperties.HTTPVerb.PUT.toString() + WalrusProperties.ObjectParameter.partNumber.toString().toLowerCase()
+ WalrusProperties.ObjectParameter.uploadId.toString().toLowerCase(), "Upload Part");
opsMap.put(OBJECT + WalrusProperties.HTTPVerb.POST.toString() + WalrusProperties.ObjectParameter.uploads.toString(), "Initiate Multipart Upload");
opsMap.put(OBJECT + WalrusProperties.HTTPVerb.POST.toString() + WalrusProperties.ObjectParameter.uploadId.toString().toLowerCase(),
"Complete Multipart Upload");
opsMap.put(OBJECT + WalrusProperties.HTTPVerb.DELETE.toString() + WalrusProperties.ObjectParameter.uploadId.toString().toLowerCase(),
"Abort Multipart Upload");
return opsMap;
}
@Override
public Object bind(final MappingHttpRequest httpRequest) throws Exception {
String servicePath = httpRequest.getServicePath();
Map bindingArguments = new HashMap();
final String operationName = getOperation(httpRequest, bindingArguments);
if (operationName == null)
throw new MethodNotAllowedException("Could not determine operation name for " + servicePath);
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.walrus.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);
final Context context = Contexts.lookup(httpRequest.getCorrelationId());
setRequiredParams(groovyMsg, context);
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());
}
LOG.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;
}
private void addLogData(BaseMessage eucaMsg, Map bindingArguments) {
if (eucaMsg instanceof WalrusRequestType) {
String operation = (String) bindingArguments.remove("Operation");
if (operation != null) {
WalrusRequestType request = (WalrusRequestType) eucaMsg;
}
}
}
private void setRequiredParams(final GroovyObject msg, Context context) throws Exception {
msg.setProperty("timeStamp", new Date());
}
protected String getOperation(MappingHttpRequest httpRequest, Map operationParams) throws Exception,
NotImplementedException, com.eucalyptus.walrus.exceptions.MethodNotAllowedException {
String[] target = null;
String path = getOperationPath(httpRequest);
String targetHost = httpRequest.getHeader(HttpHeaders.Names.HOST);
if (targetHost.contains(".walrus")) {
String bucket = targetHost.substring(0, targetHost.indexOf(".walrus"));
path = "/" + bucket + path;
}
if (path.length() > 0) {
target = WalrusUtil.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(WalrusProperties.CONTENT_LEN);
if (contentLengthString != null) {
contentLength = Long.parseLong(contentLengthString);
}
if (target == null) {
// target = service
operationKey = SERVICE + verb;
} else if (target.length == 1) {
// target = bucket
if (!target[0].equals("")) {
operationKey = BUCKET + verb;
operationParams.put("Bucket", target[0]);
operationParams.put("Operation", verb.toUpperCase() + "." + "BUCKET");
if (verb.equals(WalrusProperties.HTTPVerb.POST.toString())) {
// TODO: handle POST.
Map formFields = httpRequest.getFormFields();
String objectKey = null;
String file = (String) formFields.get(WalrusProperties.FormField.file.toString());
String authenticationHeader = "";
if (formFields.containsKey(WalrusProperties.FormField.key.toString())) {
objectKey = (String) formFields.get(WalrusProperties.FormField.key.toString());
objectKey = objectKey.replaceAll("\\$\\{filename\\}", file);
operationParams.put("Key", objectKey);
}
if (formFields.containsKey(WalrusProperties.FormField.acl.toString())) {
String acl = (String) formFields.get(WalrusProperties.FormField.acl.toString());
httpRequest.addHeader(WalrusProperties.AMZ_ACL, acl);
}
if (formFields.containsKey(WalrusProperties.FormField.redirect.toString())) {
String successActionRedirect = (String) formFields.get(WalrusProperties.FormField.redirect.toString());
operationParams.put("SuccessActionRedirect", successActionRedirect);
}
if (formFields.containsKey(WalrusProperties.FormField.success_action_redirect.toString())) {
String successActionRedirect = (String) formFields.get(WalrusProperties.FormField.success_action_redirect.toString());
operationParams.put("SuccessActionRedirect", successActionRedirect);
}
if (formFields.containsKey(WalrusProperties.FormField.success_action_status.toString())) {
Integer successActionStatus = Integer.parseInt((String) formFields.get(WalrusProperties.FormField.success_action_status.toString()));
if (successActionStatus == 200 || successActionStatus == 201)
operationParams.put("SuccessActionStatus", successActionStatus);
else
operationParams.put("SuccessActionStatus", 204);
} else {
operationParams.put("SuccessActionStatus", 204);
}
if (formFields.containsKey(WalrusProperties.CONTENT_TYPE)) {
operationParams.put("ContentType", formFields.get(WalrusProperties.CONTENT_TYPE));
}
key = target[0] + "." + objectKey;
randomKey = key + "." + Crypto.getRandom( 10 );
if (contentLengthString != null)
operationParams.put("ContentLength", (new Long(contentLength).toString()));
operationParams.put(WalrusProperties.Headers.RandomKey.toString(), randomKey);
putQueue = getWriteMessenger().interruptAllAndGetQueue(key, randomKey);
handleFirstChunk(httpRequest, (ChannelBuffer) formFields.get(WalrusProperties.IGNORE_PREFIX + "FirstDataChunk"), contentLength);
} else if (WalrusProperties.HTTPVerb.PUT.toString().equals(verb)) {
if (params.containsKey(WalrusProperties.BucketParameter.logging.toString())) {
// read logging params
getTargetBucketParams(operationParams, httpRequest);
} else if (params.containsKey(WalrusProperties.BucketParameter.versioning.toString())) {
getVersioningStatus(operationParams, httpRequest);
}
}
} else {
operationKey = SERVICE + verb;
}
} else {
// target = object
operationKey = OBJECT + verb;
String objectKey = target[1];;
try {
objectKey = WalrusUtil.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(WalrusProperties.BucketParameter.acl.toString())) {
if (verb.equals(WalrusProperties.HTTPVerb.PUT.toString())) {
if (httpRequest.containsHeader(WalrusProperties.COPY_SOURCE.toString())) {
String copySource = httpRequest.getHeader(WalrusProperties.COPY_SOURCE.toString());
try {
copySource = WalrusUtil.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 = WalrusUtil.getTarget(copySource);
String sourceObjectKey = "";
String sourceSplitOn = "";
if (sourceTarget.length > 1) {
sourceObjectKey = sourceTarget[1];
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(WalrusProperties.METADATA_DIRECTIVE.toString());
if (metaDataDirective != null) {
operationParams.put("MetadataDirective", metaDataDirective);
}
AccessControlList accessControlList;
if (contentLength > 0) {
accessControlList = null;
accessControlList = getAccessControlList(httpRequest);
} else {
accessControlList = new AccessControlList();
}
operationParams.put("AccessControlList", accessControlList);
operationKey += WalrusProperties.COPY_SOURCE.toString();
Set<String> headerNames = httpRequest.getHeaderNames();
for (String key : headerNames) {
for (WalrusProperties.CopyHeaders header : WalrusProperties.CopyHeaders.values()) {
if (key.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
// partnumber copied to operationParams later with any other object parameters
String partNumber = params.get( WalrusProperties.ObjectParameter.partNumber.toString( ) );
key = target[0] + "." + objectKey + ( partNumber == null ? "" : "." + partNumber );
randomKey = key + "." + Crypto.getRandom( 10 );
String contentType = httpRequest.getHeader(WalrusProperties.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(WalrusProperties.CONTENT_MD5);
if (contentMD5 != null)
operationParams.put("ContentMD5", contentMD5);
if (contentLengthString != null)
operationParams.put("ContentLength", (new Long(contentLength).toString()));
operationParams.put(WalrusProperties.Headers.RandomKey.toString(), randomKey);
putQueue = getWriteMessenger().interruptAllAndGetQueue(key, randomKey);
handleFirstChunk(httpRequest, contentLength);
}
} else if (verb.equals(WalrusProperties.HTTPVerb.GET.toString())) {
if (params.containsKey("torrent")) {
operationParams.put("GetTorrent", Boolean.TRUE);
} else {
operationParams.put("GetData", Boolean.TRUE);
operationParams.put("InlineData", Boolean.FALSE);
operationParams.put("GetMetaData", Boolean.TRUE);
}
Set<String> headerNames = httpRequest.getHeaderNames();
boolean isExtendedGet = false;
for (String key : headerNames) {
for (WalrusProperties.ExtendedGetHeaders header : WalrusProperties.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(WalrusProperties.GetOptionalParameters.IsCompressed.toString())) {
Boolean isCompressed = Boolean.parseBoolean(params.remove(WalrusProperties.GetOptionalParameters.IsCompressed.toString()));
operationParams.put("IsCompressed", isCompressed);
}
} else if (verb.equals(WalrusProperties.HTTPVerb.HEAD.toString())) {
operationParams.put("GetData", Boolean.FALSE);
operationParams.put("InlineData", Boolean.FALSE);
operationParams.put("GetMetaData", Boolean.TRUE);
} else if (verb.equals(WalrusProperties.HTTPVerb.POST.toString())) {
// LOG.debug("Not sure what to do here");
if (params.containsKey("uploadId")) {
operationParams.put("Parts", getPartsList(httpRequest));
}
}
}
if (params.containsKey(WalrusProperties.ObjectParameter.versionId.toString())) {
if (!verb.equals(WalrusProperties.HTTPVerb.DELETE.toString()))
operationParams.put("VersionId", params.remove(WalrusProperties.ObjectParameter.versionId.toString()));
}
}
if (verb.equals(WalrusProperties.HTTPVerb.PUT.toString()) && params.containsKey(WalrusProperties.BucketParameter.acl.toString())) {
operationParams.put("AccessControlPolicy", getAccessControlPolicy(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 (WalrusAuthenticationHandler.SecurityParameter securityParam : WalrusAuthenticationHandler.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 (WalrusProperties.ServiceParameter param : WalrusProperties.ServiceParameter.values()) {
if (keyString.toLowerCase().equals(param.toString().toLowerCase())) {
dontIncludeParam = false;
break;
}
}
} else if (operationKey.startsWith(BUCKET)) {
for (WalrusProperties.BucketParameter param : WalrusProperties.BucketParameter.values()) {
if (keyString.toLowerCase().equals(param.toString().toLowerCase())) {
dontIncludeParam = false;
break;
}
}
} else if (operationKey.startsWith(OBJECT)) {
for (WalrusProperties.ObjectParameter param : WalrusProperties.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 (WalrusProperties.SubResource subResource : WalrusProperties.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);
}
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 = "/";
}
}
throw new NotImplementedException(unsupportedOp + " is not implemented", resourceType, resource);
}
if (notAllowedOperationMap.containsKey(operationKey))
throw new com.eucalyptus.walrus.exceptions.MethodNotAllowedException();
}
if ("CreateBucket".equals(operationName)) {
String locationConstraint = getLocationConstraint(httpRequest);
if (locationConstraint != null)
operationParams.put("LocationConstraint", locationConstraint);
}
return operationName;
}
private void getTargetBucketParams(Map operationParams, MappingHttpRequest httpRequest) throws 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 BindingException("malformed access control list");
DTMNodeList grantees = xmlParser.getNodes("//TargetGrants/Grant/Grantee");
if (grantees == null)
throw new BindingException("malformed access control list");
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 BindingException("malformed access control list");
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 (Exception ex) {
throw new BindingException("Unable to parse logging configuration " + ex.getMessage());
}
}
}
private void getVersioningStatus(Map operationParams, MappingHttpRequest httpRequest) throws 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) {
throw new BindingException("Unable to parse versioning status " + ex.getMessage());
}
}
}
private void parseExtendedHeaders(Map operationParams, String headerString, String value) throws BindingException {
if (headerString.equals(WalrusProperties.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(WalrusProperties.ExtendedHeaderRangeTypes.ByteRangeStart.toString(), new Long(0));
} else {
operationParams.put(WalrusProperties.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(WalrusProperties.ExtendedHeaderRangeTypes.ByteRangeEnd.toString(), new Long(-1));
} else {
operationParams.put(WalrusProperties.ExtendedHeaderRangeTypes.ByteRangeEnd.toString(), Long.parseLong(values[1]));
}
} else if (WalrusProperties.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: " + value, e);
}
}
} else {
operationParams.put(headerString, value);
}
}
private AccessControlPolicy getAccessControlPolicy(MappingHttpRequest httpRequest) throws BindingException {
AccessControlPolicy accessControlPolicy = new AccessControlPolicy();
AccessControlList accessControlList = new AccessControlList();
ArrayList<Grant> grants = new ArrayList<Grant>();
try {
String 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 BindingException("malformed access control list");
}
DTMNodeList grantees = xmlParser.getNodes("//AccessControlList/Grant/Grantee");
if (grantees == null) {
throw new BindingException("malformed access control list");
}
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 BindingException("malformed access control list");
}
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 (Exception ex) {
throw new BindingException("Unable to parse access control policy", ex);
}
accessControlList.setGrants(grants);
accessControlPolicy.setAccessControlList(accessControlList);
return accessControlPolicy;
}
private AccessControlList getAccessControlList(MappingHttpRequest httpRequest) throws BindingException {
AccessControlList accessControlList = new AccessControlList();
ArrayList<Grant> grants = new ArrayList<Grant>();
try {
String 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 BindingException("malformed access control list");
}
DTMNodeList grantees = xmlParser.getNodes("/AccessControlList/Grant/Grantee");
if (grantees == null) {
throw new BindingException("malformed access control list");
}
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 BindingException("malformed access control list");
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 (Exception ex) {
throw new BindingException("Unable to parse access control list", ex);
}
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);
}
return partList;
}
/**
* Removes the service path for processing the bucket/key split.
*
* @param httpRequest
* @return
*/
protected String getOperationPath(MappingHttpRequest httpRequest) {
if (httpRequest.getServicePath().startsWith(walrusServicePath)) {
String opPath = httpRequest.getServicePath().replaceFirst(walrusServicePath, "");
if (!Strings.isNullOrEmpty(opPath) && !opPath.startsWith("/")) {
// The service path was not demarked with a /, e.g. /services/WalrusBackendblahblah -> blahblah
// So, don't remove the service path because that changes the semantics.
} else {
return opPath;
}
}
return httpRequest.getServicePath();
}
private String getLocationConstraint(MappingHttpRequest httpRequest) throws BindingException {
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) {
throw new BindingException("Error parsing location constraint", ex);
}
return locationConstraint;
}
private 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;
}
private void populateObjectFromBindingMap(final GroovyObject obj, final Map<String, String> paramFieldMap, final MappingHttpRequest httpRequest,
final Map bindingMap) throws BindingException {
// process headers
String aclString = httpRequest.getAndRemoveHeader(WalrusProperties.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(WalrusProperties.AMZ_META_HEADER_PREFIX)) {
MetaDataEntry metaDataEntry = new MetaDataEntry();
metaDataEntry.setName(key.substring(WalrusProperties.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));
}
}
private 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;
}
}
private 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;
}
private 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;
}
private 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;
}
private static void addAccessControlList(final GroovyObject obj, final Map<String, String> paramFieldMap, Map bindingMap, String cannedACLString) {
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>();
}
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);
}
}
private String toUpperFirst(String string) {
return string.substring(0, 1).toUpperCase().concat(string.substring(1));
}
private boolean exactMatch(JSONObject jsonObject, Map formFields, List<String> policyItemNames) {
Iterator<String> iterator = jsonObject.keys();
boolean returnValue = false;
while (iterator.hasNext()) {
String key = iterator.next();
key = key.replaceAll("\\$", "");
policyItemNames.add(key);
try {
if (jsonObject.get(key).equals(formFields.get(key)))
returnValue = true;
else
returnValue = false;
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
return returnValue;
}
private boolean partialMatch(JSONArray jsonArray, Map<String, String> formFields, List<String> policyItemNames) {
boolean returnValue = false;
if (jsonArray.size() != 3)
return false;
try {
String condition = (String) jsonArray.get(0);
String key = (String) jsonArray.get(1);
key = key.replaceAll("\\$", "");
policyItemNames.add(key);
String value = (String) jsonArray.get(2);
if (condition.contains("eq")) {
if (value.equals(formFields.get(key)))
returnValue = true;
} else if (condition.contains("starts-with")) {
if (!formFields.containsKey(key))
return false;
if (formFields.get(key).startsWith(value))
returnValue = true;
}
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
return returnValue;
}
private String getMessageString(MappingHttpRequest httpRequest) {
ChannelBuffer buffer = httpRequest.getContent();
buffer.markReaderIndex();
byte[] read = new byte[buffer.readableBytes()];
buffer.readBytes(read);
return new String(read);
}
private void handleHttpChunk(HttpChunk httpChunk) throws Exception {
ChannelBuffer buffer = httpChunk.getContent();
try {
buffer.markReaderIndex();
byte[] read = new byte[buffer.readableBytes()];
buffer.readBytes(read);
while ((putQueue != null) && (!putQueue.offer(WalrusDataMessage.DataMessage(read), 500, TimeUnit.MILLISECONDS)));
if (httpChunk.isLast()) {
while ((putQueue != null) && (!putQueue.offer(WalrusDataMessage.EOF(), 1000, TimeUnit.MILLISECONDS)));
}
} catch (Exception ex) {
throw new Exception("Error handling HTTP chunk", ex);
}
}
private void handleFirstChunk(MappingHttpRequest httpRequest, long dataLength) throws Exception {
ChannelBuffer buffer = httpRequest.getContent();
try {
putQueue.put(WalrusDataMessage.StartOfData(dataLength));
buffer.markReaderIndex();
byte[] read = new byte[buffer.readableBytes()];
buffer.readBytes(read);
putQueue.put(WalrusDataMessage.DataMessage(read));
if (!httpRequest.isChunked())
putQueue.put(WalrusDataMessage.EOF());
} catch (Exception ex) {
throw new Exception("Error handling first chunk of PUT in walrus", ex);
}
}
private void handleFirstChunk(MappingHttpRequest httpRequest, ChannelBuffer firstChunk, long dataLength) throws Exception {
try {
putQueue.put(WalrusDataMessage.StartOfData(dataLength));
byte[] read = new byte[firstChunk.readableBytes()];
firstChunk.readBytes(read);
putQueue.put(WalrusDataMessage.DataMessage(read));
if (!httpRequest.isChunked())
putQueue.put(WalrusDataMessage.EOF());
} catch (Exception ex) {
throw new Exception("Error handling first data chunk", ex);
}
}
public static synchronized WalrusDataMessenger getWriteMessenger() {
if (putMessenger == null) {
putMessenger = new WalrusDataMessenger();
}
return putMessenger;
}
class Writer extends Thread {
private ChannelBuffer firstBuffer;
private long dataLength;
public Writer(ChannelBuffer firstBuffer, long dataLength) {
this.firstBuffer = firstBuffer;
this.dataLength = dataLength;
}
public void run() {
byte[] bytes = new byte[DATA_MESSAGE_SIZE];
try {
Logs.extreme().trace("Starting upload");
putQueue.put(WalrusDataMessage.StartOfData(dataLength));
firstBuffer.markReaderIndex();
byte[] read = new byte[firstBuffer.readableBytes()];
firstBuffer.readBytes(read);
putQueue.put(WalrusDataMessage.DataMessage(read));
// putQueue.put(WalrusDataMessage.EOF());
} catch (Exception ex) {
LOG.error("Error putting data into internal transfer queue in Walrus", ex);
}
}
}
}