Author: awiner
Date: Thu Feb 26 19:29:46 2009
New Revision: 748277
URL: http://svn.apache.org/viewvc?rev=748277&view=rev
Log:
SHINDIG-938: Multipart/form-data support for Conent Upload using JsonRPC
- Patch from Sachin Shenoy
- Minor changes from me (mostly moving isMultipartForm() off JsonRpcServlet and
onto MultipartFormParser)
Added:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/CommonsFormDataItem.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/DefaultMultipartFormParser.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/FormDataItem.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/MultipartFormParser.java
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/multipart/
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/multipart/DefaultMultipartFormParserTest.java
Modified:
incubator/shindig/trunk/java/common/pom.xml
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/BaseRequestItem.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/DefaultHandlerRegistry.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/JsonRpcServlet.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RequestItem.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RpcHandler.java
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/BaseRequestItemTest.java
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/DefaultHandlerRegistryTest.java
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/JsonRpcServletTest.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/SocialRequestItem.java
incubator/shindig/trunk/pom.xml
Modified: incubator/shindig/trunk/java/common/pom.xml
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/pom.xml?rev=748277&r1=748276&r2=748277&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/pom.xml (original)
+++ incubator/shindig/trunk/java/common/pom.xml Thu Feb 26 19:29:46 2009
@@ -85,6 +85,10 @@
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
+ <groupId>commons-fileupload</groupId>
+ <artifactId>commons-fileupload</artifactId>
+ </dependency>
+ <dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/BaseRequestItem.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/BaseRequestItem.java?rev=748277&r1=748276&r2=748277&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/BaseRequestItem.java
(original)
+++
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/BaseRequestItem.java
Thu Feb 26 19:29:46 2009
@@ -22,6 +22,7 @@
import org.apache.shindig.protocol.conversion.BeanJsonConverter;
import org.apache.shindig.protocol.model.FilterOperation;
import org.apache.shindig.protocol.model.SortOrder;
+import org.apache.shindig.protocol.multipart.FormDataItem;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
@@ -47,6 +48,7 @@
protected final SecurityToken token;
final BeanConverter converter;
final Map<String,Object> parameters;
+ final Map<String, FormDataItem> formItems;
final BeanJsonConverter jsonConverter;
public BaseRequestItem(Map<String, String[]> parameters,
@@ -66,9 +68,11 @@
}
}
this.jsonConverter = jsonConverter;
+ this.formItems = null;
}
public BaseRequestItem(JSONObject parameters,
+ Map<String, FormDataItem> formItems,
SecurityToken token,
BeanConverter converter,
BeanJsonConverter jsonConverter) {
@@ -83,6 +87,7 @@
}
this.token = token;
this.converter = converter;
+ this.formItems = formItems;
} catch (JSONException je) {
throw new ProtocolException(ResponseError.INTERNAL_ERROR,
je.getMessage(), je);
}
@@ -272,4 +277,12 @@
this.parameters.put(paramName, paramValue);
}
}
+
+ public FormDataItem getFormMimePart(String partName) {
+ if (formItems != null) {
+ return formItems.get(partName);
+ } else {
+ return null;
+ }
+ }
}
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/DefaultHandlerRegistry.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/DefaultHandlerRegistry.java?rev=748277&r1=748276&r2=748277&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/DefaultHandlerRegistry.java
(original)
+++
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/DefaultHandlerRegistry.java
Thu Feb 26 19:29:46 2009
@@ -24,6 +24,7 @@
import org.apache.shindig.common.util.ImmediateFuture;
import org.apache.shindig.protocol.conversion.BeanConverter;
import org.apache.shindig.protocol.conversion.BeanJsonConverter;
+import org.apache.shindig.protocol.multipart.FormDataItem;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -280,10 +281,11 @@
this.methodCaller = methodCaller;
}
- public Future<?> execute(JSONObject rpc, SecurityToken token,
BeanConverter converter) {
+ public Future<?> execute(JSONObject rpc, Map<String, FormDataItem>
formItems,
+ SecurityToken token, BeanConverter converter) {
try {
JSONObject params = rpc.has("params") ? (JSONObject)rpc.get("params")
: new JSONObject();
- RequestItem item = methodCaller.getRpcRequestItem(params, token,
beanJsonConverter);
+ RequestItem item = methodCaller.getRpcRequestItem(params, formItems,
token, beanJsonConverter);
listener.executing(item);
return methodCaller.call(handlerProvider.get(), item);
@@ -306,8 +308,9 @@
this.rpc = rpc;
}
- public Future<?> execute(SecurityToken st, BeanConverter converter) {
- return handler.execute(rpc, st, converter);
+ public Future<?> execute(Map<String, FormDataItem> formItems,
SecurityToken st,
+ BeanConverter converter) {
+ return handler.execute(rpc, formItems, st, converter);
}
}
@@ -410,7 +413,7 @@
restRequestItemConstructor = requestItemType.getConstructor(Map.class,
SecurityToken.class, BeanConverter.class, BeanJsonConverter.class);
rpcRequestItemConstructor =
requestItemType.getConstructor(JSONObject.class,
- SecurityToken.class, BeanConverter.class, BeanJsonConverter.class);
+ Map.class, SecurityToken.class, BeanConverter.class,
BeanJsonConverter.class);
}
public RequestItem getRestRequestItem(Map<String, String[]> params,
SecurityToken token,
@@ -418,15 +421,30 @@
return getRequestItem(params, token, converter, jsonConverter,
restRequestItemConstructor);
}
- public RequestItem getRpcRequestItem(JSONObject params, SecurityToken
token, BeanJsonConverter converter) {
- return getRequestItem(params, token, converter, converter,
rpcRequestItemConstructor);
+ public RequestItem getRpcRequestItem(JSONObject params, Map<String,
FormDataItem> formItems,
+ SecurityToken token, BeanJsonConverter converter) {
+ return getRequestItem(params, formItems, token, converter, converter,
rpcRequestItemConstructor);
+ }
+
+ private RequestItem getRequestItem(Object params, Map<String,
FormDataItem> formItems,
+ SecurityToken token, BeanConverter converter, BeanJsonConverter
jsonConverter,
+ Constructor<?> constructor) {
+ try {
+ return (RequestItem) constructor.newInstance(params, formItems, token,
converter,
+ jsonConverter);
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
}
private RequestItem getRequestItem(Object params, SecurityToken token,
BeanConverter converter,
BeanJsonConverter jsonConverter, Constructor<?> constructor) {
try {
- return (RequestItem) constructor.newInstance(params, token, converter,
- jsonConverter);
+ return (RequestItem) constructor.newInstance(params, token,
converter, jsonConverter);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
@@ -488,7 +506,8 @@
this.error = error;
}
- public Future<?> execute(SecurityToken token, BeanConverter converter) {
+ public Future<?> execute(Map<String, FormDataItem> formItems,
SecurityToken token,
+ BeanConverter converter) {
return ImmediateFuture.errorInstance(error);
}
}
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/JsonRpcServlet.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/JsonRpcServlet.java?rev=748277&r1=748276&r2=748277&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/JsonRpcServlet.java
(original)
+++
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/JsonRpcServlet.java
Thu Feb 26 19:29:46 2009
@@ -19,9 +19,12 @@
import org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.common.util.JsonConversionUtil;
+import org.apache.shindig.protocol.multipart.FormDataItem;
+import org.apache.shindig.protocol.multipart.MultipartFormParser;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import com.google.inject.Inject;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
@@ -44,6 +47,20 @@
*/
public class JsonRpcServlet extends ApiServlet {
+ /**
+ * In a multipart request, the form item with field name "request" will
contain the
+ * actual request, per the proposed Opensocial 0.9 specification.
+ */
+ public static final String REQUEST_PARAM = "request";
+ private static final String CONTENT_TYPE_JSON = "application/json";
+
+ private MultipartFormParser formParser;
+
+ @Inject
+ void setMultipartFormParser(MultipartFormParser formParser) {
+ this.formParser = formParser;
+ }
+
@Override
protected void doGet(HttpServletRequest servletRequest, HttpServletResponse
servletResponse)
throws IOException {
@@ -56,7 +73,7 @@
try {
setCharacterEncodings(servletRequest, servletResponse);
JSONObject request = JsonConversionUtil.fromRequest(servletRequest);
- dispatch(request, servletRequest, servletResponse, token);
+ dispatch(request, null, servletRequest, servletResponse, token);
} catch (JSONException je) {
// FIXME
}
@@ -72,26 +89,53 @@
}
setCharacterEncodings(servletRequest, servletResponse);
- servletResponse.setContentType("application/json");
+ servletResponse.setContentType(CONTENT_TYPE_JSON);
try {
- String content = IOUtils.toString(servletRequest.getInputStream(),
- servletRequest.getCharacterEncoding());
+ String content = null;
+ Map<String, FormDataItem> formItems = new HashMap<String,
FormDataItem>();
+
+ if (formParser.isMultipartContent(servletRequest)) {
+ for (FormDataItem item : formParser.parse(servletRequest)) {
+ if (item.isFormField() && content == null) {
+ // As per spec, in case of a multipart/form-data content, there
will be one form field
+ // with field name as "request". It will contain the json request.
Any further form
+ // field or file item will not be parsed out, but will be exposed
via getFormItem
+ // method of RequestItem. Here we are lenient where a mime part
which has content type
+ // application/json will be considered as request.
+ if (REQUEST_PARAM.equals(item.getFieldName()) ||
+ CONTENT_TYPE_JSON.equals(item.getContentType())) {
+ content = IOUtils.toString(item.getInputStream());
+ }
+ } else {
+ formItems.put(item.getFieldName(), item);
+ }
+ }
+
+ if (content == null) {
+ content = "";
+ }
+ } else {
+ content = IOUtils.toString(servletRequest.getInputStream(),
+ servletRequest.getCharacterEncoding());
+ }
+
if ((content.indexOf('[') != -1) && content.indexOf('[') <
content.indexOf('{')) {
// Is a batch
JSONArray batch = new JSONArray(content);
- dispatchBatch(batch, servletRequest, servletResponse, token);
+ dispatchBatch(batch, formItems, servletRequest, servletResponse,
token);
} else {
JSONObject request = new JSONObject(content);
- dispatch(request, servletRequest, servletResponse, token);
+ dispatch(request, formItems, servletRequest, servletResponse, token);
}
} catch (JSONException je) {
sendBadRequest(je, servletResponse);
}
}
- protected void dispatchBatch(JSONArray batch, HttpServletRequest
servletRequest,
- HttpServletResponse servletResponse, SecurityToken token) throws
JSONException, IOException {
+ protected void dispatchBatch(JSONArray batch, Map<String, FormDataItem>
formItems ,
+ HttpServletRequest servletRequest, HttpServletResponse servletResponse,
+ SecurityToken token) throws JSONException, IOException {
// Use linked hash map to preserve order
List<Future<?>> responses =
Lists.newArrayListWithExpectedSize(batch.length());
@@ -101,7 +145,7 @@
// into single requests.
for (int i = 0; i < batch.length(); i++) {
JSONObject batchObj = batch.getJSONObject(i);
- responses.add(getHandler(batchObj, servletRequest).execute(token,
jsonConverter));
+ responses.add(getHandler(batchObj, servletRequest).execute(formItems,
token, jsonConverter));
}
// Resolve each Future into a response.
@@ -119,15 +163,16 @@
jsonConverter.append(servletResponse.getWriter(), result);
}
- protected void dispatch(JSONObject request, HttpServletRequest
servletRequest,
- HttpServletResponse servletResponse, SecurityToken token) throws
JSONException, IOException {
+ protected void dispatch(JSONObject request, Map<String, FormDataItem>
formItems,
+ HttpServletRequest servletRequest, HttpServletResponse servletResponse,
+ SecurityToken token) throws JSONException, IOException {
String key = null;
if (request.has("id")) {
key = request.getString("id");
}
// getRpcHandler never returns null
- Future<?> future = getHandler(request, servletRequest).execute(token,
jsonConverter);
+ Future<?> future = getHandler(request, servletRequest).execute(formItems,
token, jsonConverter);
// Resolve each Future into a response.
// TODO: should use shared deadline across each request
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RequestItem.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RequestItem.java?rev=748277&r1=748276&r2=748277&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RequestItem.java
(original)
+++
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RequestItem.java
Thu Feb 26 19:29:46 2009
@@ -20,6 +20,7 @@
import org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.protocol.model.FilterOperation;
import org.apache.shindig.protocol.model.SortOrder;
+import org.apache.shindig.protocol.multipart.FormDataItem;
import java.util.Date;
import java.util.List;
@@ -77,4 +78,6 @@
String getParameter(String paramName, String defaultValue);
List<String> getListParameter(String paramName);
+
+ FormDataItem getFormMimePart(String partName);
}
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RpcHandler.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RpcHandler.java?rev=748277&r1=748276&r2=748277&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RpcHandler.java
(original)
+++
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RpcHandler.java
Thu Feb 26 19:29:46 2009
@@ -20,7 +20,9 @@
import org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.protocol.conversion.BeanConverter;
+import org.apache.shindig.protocol.multipart.FormDataItem;
+import java.util.Map;
import java.util.concurrent.Future;
/**
@@ -30,7 +32,7 @@
/**
* Handle the request and return a Future from which the response object
- * can be retrieved
+ * can be retrieved.
*/
- Future<?> execute(SecurityToken st, BeanConverter converter);
+ Future<?> execute(Map<String, FormDataItem> formItems, SecurityToken st,
BeanConverter converter);
}
Added:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/CommonsFormDataItem.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/CommonsFormDataItem.java?rev=748277&view=auto
==============================================================================
---
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/CommonsFormDataItem.java
(added)
+++
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/CommonsFormDataItem.java
Thu Feb 26 19:29:46 2009
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.apache.shindig.protocol.multipart;
+
+import org.apache.commons.fileupload.FileItem;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Implementation of FormDataItem using Apache commons FileItem.
+ */
+class CommonsFormDataItem implements FormDataItem {
+ private final FileItem fileItem;
+
+ CommonsFormDataItem(FileItem fileItem) {
+ this.fileItem = fileItem;
+ }
+
+ public byte[] get() {
+ return fileItem.get();
+ }
+
+ public String getContentType() {
+ return fileItem.getContentType();
+ }
+
+ public String getFieldName() {
+ return fileItem.getFieldName();
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return fileItem.getInputStream();
+ }
+
+ public String getName() {
+ return fileItem.getName();
+ }
+
+ public long getSize() {
+ return fileItem.getSize();
+ }
+
+ public boolean isFormField() {
+ return fileItem.isFormField();
+ }
+}
Added:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/DefaultMultipartFormParser.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/DefaultMultipartFormParser.java?rev=748277&view=auto
==============================================================================
---
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/DefaultMultipartFormParser.java
(added)
+++
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/DefaultMultipartFormParser.java
Thu Feb 26 19:29:46 2009
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.apache.shindig.protocol.multipart;
+
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.FileItemFactory;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+
+import java.io.IOException;
+import java.net.UnknownServiceException;
+import java.util.Collection;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Implementation of MultipartFormParser using Apache Commons file upload.
+ */
+public class DefaultMultipartFormParser implements MultipartFormParser {
+ private static final String MULTIPART = "multipart/";
+
+ public Collection<FormDataItem> parse(HttpServletRequest servletRequest)
+ throws IOException {
+ FileItemFactory factory = new DiskFileItemFactory();
+ ServletFileUpload upload = new ServletFileUpload(factory);
+
+ try {
+ @SuppressWarnings("unchecked")
+ List<FileItem> fileItems = upload.parseRequest(servletRequest);
+ return convertToFormData(fileItems);
+ } catch (FileUploadException e) {
+ UnknownServiceException use = new UnknownServiceException("File upload
error.");
+ use.initCause(e);
+ throw use;
+ }
+ }
+
+ private Collection<FormDataItem> convertToFormData(List<FileItem> fileItems)
{
+ List<FormDataItem> formDataItems =
+ Lists.newArrayListWithCapacity(fileItems.size());
+ for (FileItem item : fileItems) {
+ formDataItems.add(new CommonsFormDataItem(item));
+ }
+
+ return formDataItems;
+ }
+
+ public boolean isMultipartContent(HttpServletRequest request) {
+ if (!"POST".equals(request.getMethod())) {
+ return false;
+ }
+ String contentType = request.getContentType();
+ if (contentType == null) {
+ return false;
+ }
+ if (contentType.toLowerCase().startsWith(MULTIPART)) {
+ return true;
+ }
+ return false;
+ }
+}
Added:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/FormDataItem.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/FormDataItem.java?rev=748277&view=auto
==============================================================================
---
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/FormDataItem.java
(added)
+++
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/FormDataItem.java
Thu Feb 26 19:29:46 2009
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.apache.shindig.protocol.multipart;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Interface to represent an field item in multipart/form-data.
+ */
+public interface FormDataItem {
+
+ /**
+ * Returns the Content type of the field item.
+ *
+ * @return content type
+ */
+ String getContentType();
+
+ /**
+ * The size of the content stored in this field item.
+ *
+ * @return size of the content
+ */
+ long getSize();
+
+ /**
+ * Returns an InputStream from which the content of the field item can be
+ * read.
+ *
+ * @return InputStream to the content of the field item.
+ * @throws IOException
+ */
+ InputStream getInputStream() throws IOException;
+
+ /**
+ * Returns the content of the field item.
+ *
+ * @return content of the field item
+ */
+ byte[] get();
+
+ /**
+ * Name of the uploaded file, if the item represents file upload.
+ * This will be only valid when {...@link #isFormField()} returns false.
+ *
+ * @return name of the uploaded file
+ */
+ String getName();
+
+ /**
+ * Field name of this field item. Can be used to identify a field by name but
+ * as per RFC this need not be unique.
+ *
+ * @return name of the field
+ */
+ String getFieldName();
+
+ /**
+ * Used to identify if the field item represents a file upload or a regular
+ * form field.
+ *
+ * @return true if it is a regular form field
+ */
+ boolean isFormField();
+}
Added:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/MultipartFormParser.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/MultipartFormParser.java?rev=748277&view=auto
==============================================================================
---
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/MultipartFormParser.java
(added)
+++
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/multipart/MultipartFormParser.java
Thu Feb 26 19:29:46 2009
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.apache.shindig.protocol.multipart;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import javax.servlet.http.HttpServletRequest;
+
+import com.google.inject.ImplementedBy;
+
+/**
+ * Class providing a facade over multipart form handling.
+ */
+...@implementedby(DefaultMultipartFormParser.class)
+public interface MultipartFormParser {
+ /** Parse a request into a list of data items */
+ Collection<FormDataItem> parse(HttpServletRequest request) throws
IOException;
+
+ /**
+ * @return true if the request requires multipart parsing.
+ */
+ boolean isMultipartContent(HttpServletRequest request);
+}
Modified:
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/BaseRequestItemTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/BaseRequestItemTest.java?rev=748277&r1=748276&r2=748277&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/BaseRequestItemTest.java
(original)
+++
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/BaseRequestItemTest.java
Thu Feb 26 19:29:46 2009
@@ -106,7 +106,7 @@
"userId:john.doe," +
"groupId:@self," +
"fields:[huey,dewey,louie]" +
- "}"), FAKE_TOKEN, converter, converter);
+ "}"), null, FAKE_TOKEN, converter, converter);
assertEquals(Lists.newArrayList("huey", "dewey", "louie"),
request.getListParameter("fields"));
}
Modified:
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/DefaultHandlerRegistryTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/DefaultHandlerRegistryTest.java?rev=748277&r1=748276&r2=748277&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/DefaultHandlerRegistryTest.java
(original)
+++
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/DefaultHandlerRegistryTest.java
Thu Feb 26 19:29:46 2009
@@ -82,7 +82,7 @@
JSONObject rpc = new JSONObject("{method : makebelieve.get}");
RpcHandler rpcHandler = registry.getRpcHandler(rpc);
try {
- Future<?> future = rpcHandler.execute(null, null);
+ Future<?> future = rpcHandler.execute(null, null, null);
future.get();
fail("Expect exception for missing method");
} catch (ExecutionException t) {
@@ -124,7 +124,7 @@
public void testRpcWithInputClassThatIsntRequestItem() throws Exception {
JSONObject rpc = new JSONObject("{ method : test.echo, params: {value:
'Bob' }}");
RpcHandler handler = registry.getRpcHandler(rpc);
- Future<?> future = handler.execute(null, converter);
+ Future<?> future = handler.execute(null, null, converter);
assertEquals(future.get(), TestHandler.ECHO_PREFIX + "Bob");
}
@@ -138,7 +138,7 @@
public void testNoArgumentClass() throws Exception {
JSONObject rpc = new JSONObject("{ method : test.noArg }");
RpcHandler handler = registry.getRpcHandler(rpc);
- Future<?> future = handler.execute(null, converter);
+ Future<?> future = handler.execute(null, null, converter);
assertEquals(future.get(), TestHandler.NO_ARG_RESPONSE);
}
@@ -146,7 +146,7 @@
// Test calling a handler method which does not return a future
JSONObject rpc = new JSONObject("{ method : test.exception }");
RpcHandler handler = registry.getRpcHandler(rpc);
- Future<?> future = handler.execute(null, null);
+ Future<?> future = handler.execute(null, null, null);
try {
future.get();
fail("Service method did not produce NullPointerException from Future");
@@ -159,7 +159,7 @@
// Test calling a handler method which does not return a future
JSONObject rpc = new JSONObject("{ method : test.futureException }");
RpcHandler handler = registry.getRpcHandler(rpc);
- Future<?> future = handler.execute(null, null);
+ Future<?> future = handler.execute(null, null, null);
try {
future.get();
fail("Service method did not produce ExecutionException from Future");
Modified:
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/JsonRpcServletTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/JsonRpcServletTest.java?rev=748277&r1=748276&r2=748277&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/JsonRpcServletTest.java
(original)
+++
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/JsonRpcServletTest.java
Thu Feb 26 19:29:46 2009
@@ -21,26 +21,35 @@
import org.apache.shindig.common.testing.FakeGadgetToken;
import org.apache.shindig.protocol.conversion.BeanConverter;
import org.apache.shindig.protocol.conversion.BeanJsonConverter;
+import org.apache.shindig.protocol.multipart.FormDataItem;
+import org.apache.shindig.protocol.multipart.MultipartFormParser;
-import com.google.common.collect.ImmutableMap;
-import com.google.inject.Guice;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.isA;
+import static org.easymock.classextension.EasyMock.reset;
import org.easymock.IMocksControl;
import org.easymock.classextension.EasyMock;
-import junit.framework.TestCase;
-
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import junit.framework.TestCase;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Guice;
+
/**
*
*/
@@ -49,10 +58,15 @@
private static final FakeGadgetToken FAKE_GADGET_TOKEN = new
FakeGadgetToken()
.setOwnerId("john.doe").setViewerId("john.doe");
+ private static final String IMAGE_FIELDNAME = "profile-photo";
+ private static final byte[] IMAGE_DATA = "image data".getBytes();
+ private static final String IMAGE_TYPE = "image/jpeg";
+
private HttpServletRequest req;
private HttpServletResponse res;
private JsonRpcServlet servlet;
-
+ private MultipartFormParser multipartFormParser;
+
private BeanJsonConverter jsonConverter;
private BeanConverter xmlConverter;
protected BeanConverter atomConverter;
@@ -71,6 +85,10 @@
xmlConverter = mockControl.createMock(BeanConverter.class);
atomConverter = mockControl.createMock(BeanConverter.class);
+ multipartFormParser = mockControl.createMock(MultipartFormParser.class);
+
EasyMock.expect(multipartFormParser.isMultipartContent(req)).andStubReturn(false);
+ servlet.setMultipartFormParser(multipartFormParser);
+
HandlerRegistry registry = new DefaultHandlerRegistry(null,
Collections.<Object>singleton(handler), jsonConverter,
new HandlerExecutionListener.NoOpHandlerExecutionListener());
@@ -93,8 +111,8 @@
public void testMethodRecognition() throws Exception {
setupRequest("{method:test.get,id:id,params:{userId:5,groupId:@self}}");
- EasyMock.expect(res.getWriter()).andReturn(writer);
- EasyMock.expectLastCall();
+ expect(res.getWriter()).andReturn(writer);
+ expectLastCall();
mockControl.replay();
servlet.service(req, res);
@@ -103,11 +121,90 @@
JsonAssert.assertJsonEquals("{id: 'id', data: {foo:'bar'}}", getOutput());
}
+ public void testPostMultipartFormData() throws Exception {
+ reset(multipartFormParser);
+
+ handler.setMock(new TestHandler() {
+ @Override
+ public Object get(RequestItem req) {
+ FormDataItem item = req.getFormMimePart(IMAGE_FIELDNAME);
+ return ImmutableMap.of("image-data", new String(item.get()),
+ "image-type", item.getContentType(),
+ "image-ref", req.getParameter("image-ref"));
+ }
+ });
+ expect(req.getMethod()).andStubReturn("POST");
+ expect(req.getAttribute(isA(String.class))).andReturn(FAKE_GADGET_TOKEN);
+ expect(req.getCharacterEncoding()).andStubReturn("UTF-8");
+ res.setCharacterEncoding("UTF-8");
+ res.setContentType("application/json");
+
+ List<FormDataItem> formItems = new ArrayList<FormDataItem>();
+ String request = "{method:test.get,id:id,params:" +
+ "{userId:5,groupId:@self,image-ref:@" + IMAGE_FIELDNAME + "}}";
+ formItems.add(mockFormDataItem(JsonRpcServlet.REQUEST_PARAM,
"application/json",
+ request.getBytes(), true));
+ formItems.add(mockFormDataItem(IMAGE_FIELDNAME, IMAGE_TYPE, IMAGE_DATA,
false));
+ expect(multipartFormParser.isMultipartContent(req)).andReturn(true);
+ expect(multipartFormParser.parse(req)).andReturn(formItems);
+ expect(res.getWriter()).andReturn(writer);
+ expectLastCall();
+
+ mockControl.replay();
+ servlet.service(req, res);
+ mockControl.verify();
+
+ JsonAssert.assertJsonEquals("{id: 'id', data: {image-data:'" + new
String(IMAGE_DATA) +
+ "', image-type:'" + IMAGE_TYPE + "', image-ref:'@" + IMAGE_FIELDNAME +
"'}}", getOutput());
+ }
+
+ /**
+ * Tests whether mime part with content type appliction/json is picked up as
+ * request even when its fieldname is not "request".
+ */
+ public void testPostMultipartFormDataWithNoExplicitRequestField() throws
Exception {
+ reset(multipartFormParser);
+
+ handler.setMock(new TestHandler() {
+ @Override
+ public Object get(RequestItem req) {
+ FormDataItem item = req.getFormMimePart(IMAGE_FIELDNAME);
+ return ImmutableMap.of("image-data", new String(item.get()),
+ "image-type", item.getContentType(),
+ "image-ref", req.getParameter("image-ref"));
+ }
+ });
+ expect(req.getMethod()).andStubReturn("POST");
+ expect(req.getAttribute(isA(String.class))).andReturn(FAKE_GADGET_TOKEN);
+ expect(req.getCharacterEncoding()).andStubReturn("UTF-8");
+ res.setCharacterEncoding("UTF-8");
+ res.setContentType("application/json");
+
+ List<FormDataItem> formItems = new ArrayList<FormDataItem>();
+ String request = "{method:test.get,id:id,params:" +
+ "{userId:5,groupId:@self,image-ref:@" + IMAGE_FIELDNAME + "}}";
+ formItems.add(mockFormDataItem(IMAGE_FIELDNAME, IMAGE_TYPE, IMAGE_DATA,
false));
+ formItems.add(mockFormDataItem("json", "application/json",
+ request.getBytes(), true));
+ expect(multipartFormParser.isMultipartContent(req)).andReturn(true);
+ expect(multipartFormParser.parse(req)).andReturn(formItems);
+ expect(res.getWriter()).andReturn(writer);
+ expectLastCall();
+
+ mockControl.replay();
+ servlet.service(req, res);
+ mockControl.verify();
+
+ JsonAssert.assertJsonEquals("{id: 'id', data: {image-data:'" + new
String(IMAGE_DATA) +
+ "', image-type:'" + IMAGE_TYPE + "', image-ref:'@" + IMAGE_FIELDNAME +
"'}}", getOutput());
+ }
+
+
public void testInvalidService() throws Exception {
setupRequest("{method:junk.get,id:id,params:{userId:5,groupId:@self}}");
- EasyMock.expect(res.getWriter()).andReturn(writer);
- EasyMock.expectLastCall();
+ expect(res.getWriter()).andReturn(writer);
+ expectLastCall();
mockControl.replay();
servlet.service(req, res);
@@ -126,12 +223,13 @@
public void testFailedRequest() throws Exception {
setupRequest("{id:id,method:test.futureException}");
- EasyMock.expect(res.getWriter()).andReturn(writer);
- EasyMock.expectLastCall();
+ expect(res.getWriter()).andReturn(writer);
+ expectLastCall();
mockControl.replay();
servlet.service(req, res);
-
+ mockControl.verify();
+
JsonAssert.assertJsonEquals(
"{id:id,error:{message:'badRequest: FAILURE_MESSAGE',code:400}}",
getOutput());
}
@@ -139,26 +237,27 @@
public void testBasicBatch() throws Exception {
setupRequest("[{method:test.get,id:'1'},{method:test.get,id:'2'}]");
- EasyMock.expect(res.getWriter()).andReturn(writer);
- EasyMock.expectLastCall();
+ expect(res.getWriter()).andReturn(writer);
+ expectLastCall();
mockControl.replay();
servlet.service(req, res);
+ mockControl.verify();
JsonAssert.assertJsonEquals("[{id:'1',data:{foo:'bar'}},{id:'2',data:{foo:'bar'}}]",
getOutput());
}
public void testGetExecution() throws Exception {
- EasyMock.expect(req.getParameterMap()).andStubReturn(
+ expect(req.getParameterMap()).andStubReturn(
ImmutableMap.of("method", new String[]{"test.get"}, "id", new
String[]{"1"}));
- EasyMock.expect(req.getMethod()).andStubReturn("GET");
-
EasyMock.expect(req.getAttribute(EasyMock.isA(String.class))).andReturn(FAKE_GADGET_TOKEN);
- EasyMock.expect(req.getCharacterEncoding()).andStubReturn("UTF-8");
+ expect(req.getMethod()).andStubReturn("GET");
+ expect(req.getAttribute(isA(String.class))).andReturn(FAKE_GADGET_TOKEN);
+ expect(req.getCharacterEncoding()).andStubReturn("UTF-8");
res.setCharacterEncoding("UTF-8");
- EasyMock.expect(res.getWriter()).andReturn(writer);
- EasyMock.expectLastCall();
+ expect(res.getWriter()).andReturn(writer);
+ expectLastCall();
mockControl.replay();
servlet.service(req, res);
@@ -176,12 +275,26 @@
}
};
- EasyMock.expect(req.getInputStream()).andStubReturn(stream);
- EasyMock.expect(req.getMethod()).andStubReturn("POST");
-
EasyMock.expect(req.getAttribute(EasyMock.isA(String.class))).andReturn(FAKE_GADGET_TOKEN);
- EasyMock.expect(req.getCharacterEncoding()).andStubReturn("UTF-8");
+ expect(req.getInputStream()).andStubReturn(stream);
+ expect(req.getMethod()).andStubReturn("POST");
+ expect(req.getAttribute(isA(String.class))).andReturn(FAKE_GADGET_TOKEN);
+ expect(req.getCharacterEncoding()).andStubReturn("UTF-8");
+ expect(req.getContentType()).andStubReturn("application/json");
res.setCharacterEncoding("UTF-8");
res.setContentType("application/json");
}
+ private FormDataItem mockFormDataItem(String fieldName, String contentType,
byte content[],
+ boolean isFormField) throws IOException {
+ InputStream in = new ByteArrayInputStream(content);
+ FormDataItem formDataItem = mockControl.createMock(FormDataItem.class);
+ expect(formDataItem.getContentType()).andStubReturn(contentType);
+ expect(formDataItem.getSize()).andStubReturn((long) content.length);
+ expect(formDataItem.get()).andStubReturn(content);
+ expect(formDataItem.getFieldName()).andStubReturn(fieldName);
+ expect(formDataItem.isFormField()).andStubReturn(isFormField);
+ expect(formDataItem.getInputStream()).andStubReturn(in);
+ return formDataItem;
+ }
+
}
Added:
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/multipart/DefaultMultipartFormParserTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/multipart/DefaultMultipartFormParserTest.java?rev=748277&view=auto
==============================================================================
---
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/multipart/DefaultMultipartFormParserTest.java
(added)
+++
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/multipart/DefaultMultipartFormParserTest.java
Thu Feb 26 19:29:46 2009
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.apache.shindig.protocol.multipart;
+
+import org.apache.shindig.common.testing.FakeHttpServletRequest;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import junit.framework.TestCase;
+
+import com.google.common.collect.Lists;
+
+public class DefaultMultipartFormParserTest extends TestCase {
+
+ private static final String REQUEST_FIELDNAME = "request";
+ private static final String REQUEST_DATA = "{name: 'HelloWorld'}";
+
+ private static final String ALBUM_IMAGE_FIELDNAME = "album-image";
+ private static final String ALBUM_IMAGE_FILENAME = "album-image.jpg";
+ private static final String ALBUM_IMAGE_DATA = "album image data";
+ private static final String ALBUM_IMAGE_TYPE = "image/jpeg";
+
+ private static final String PROFILE_IMAGE_FIELDNAME = "profile-image";
+ private static final String PROFILE_IMAGE_FILENAME = "profile-image.jpg";
+ private static final String PROFILE_IMAGE_DATA = "profile image data";
+ private static final String PROFILE_IMAGE_TYPE = "image/png";
+
+ private MultipartFormParser multipartFormParser;
+ private HttpServletRequest request;
+
+ @Override protected void setUp() throws Exception {
+ multipartFormParser = new DefaultMultipartFormParser();
+ }
+
+ /**
+ * Test that requests must be both POST and have a multipart
+ * content type.
+ */
+ public void testIsMultipartContent() {
+ FakeHttpServletRequest request = new FakeHttpServletRequest();
+
+ request.setMethod("GET");
+ assertFalse(multipartFormParser.isMultipartContent(request));
+
+ request.setMethod("POST");
+ assertFalse(multipartFormParser.isMultipartContent(request));
+
+ request.setContentType("multipart/form-data");
+ assertTrue(multipartFormParser.isMultipartContent(request));
+
+ request.setMethod("GET");
+ assertFalse(multipartFormParser.isMultipartContent(request));
+}
+
+ /**
+ * Helper class to create the multipart/form-data body of the POST request.
+ */
+ private static class MultipartFormBuilder {
+ private final String boundary;
+ private final StringBuilder packet = new StringBuilder();
+ private static final String BOUNDARY = "--abcdefgh";
+
+ public MultipartFormBuilder() {
+ this(BOUNDARY);
+ }
+
+ public MultipartFormBuilder(String boundary) {
+ this.boundary = boundary;
+ }
+
+ public String getContentType() {
+ return "multipart/form-data; boundary=" + boundary;
+ }
+
+ public byte[] build() {
+ write("--");
+ write(boundary);
+ write("--");
+ return packet.toString().getBytes();
+ }
+
+ public void addFileItem(String fieldName, String fileName, String content,
+ String contentType) {
+ writeBoundary();
+
+ write("Content-Disposition: form-data; name=\"" + fieldName + "\";
filename=\"" +
+ fileName + "\"");
+ write("\r\n");
+ write("Content-Type: " + contentType);
+ write("\r\n\r\n");
+ write(content);
+ write("\r\n");
+ }
+
+ public void addFormField(String fieldName, String content) {
+ addFormField(fieldName, content, null);
+ }
+
+ public void addFormField(String fieldName, String content, String
contentType) {
+ writeBoundary();
+
+ write("Content-Disposition: form-data; name=\"" + fieldName + "\"");
+ if (contentType != null) {
+ write("\r\n");
+ write("Content-Type: " + contentType);
+ }
+ write("\r\n\r\n");
+ write(content);
+ write("\r\n");
+ }
+
+ private void writeBoundary() {
+ write("--");
+ write(boundary);
+ write("\r\n");
+ }
+
+ private void write(String content) {
+ packet.append(content);
+ }
+ }
+
+ private void setupRequest(byte[] postData, String contentType) throws
IOException {
+ FakeHttpServletRequest fakeReq = new
FakeHttpServletRequest("/social/rest", "", "");
+ fakeReq.setPostData(postData);
+ fakeReq.setContentType(contentType);
+ request = fakeReq;
+ }
+
+ public void testSingleFileItem() throws Exception {
+ MultipartFormBuilder builder = new MultipartFormBuilder();
+ builder.addFileItem(ALBUM_IMAGE_FIELDNAME, ALBUM_IMAGE_FILENAME,
ALBUM_IMAGE_DATA,
+ ALBUM_IMAGE_TYPE);
+ setupRequest(builder.build(), builder.getContentType());
+
+ List<FormDataItem> formItems =
+ Lists.newArrayList(multipartFormParser.parse(request));
+
+ assertEquals(1, formItems.size());
+ FormDataItem formItem = formItems.get(0);
+ assertFalse(formItem.isFormField());
+ assertEquals(ALBUM_IMAGE_FIELDNAME, formItem.getFieldName());
+ assertEquals(ALBUM_IMAGE_FILENAME, formItem.getName());
+ assertEquals(ALBUM_IMAGE_TYPE, formItem.getContentType());
+ assertEquals(ALBUM_IMAGE_DATA, new String(formItem.get()));
+ }
+
+ public void testSingleRequest() throws Exception {
+ MultipartFormBuilder builder = new MultipartFormBuilder();
+ builder.addFormField(REQUEST_FIELDNAME, REQUEST_DATA);
+ setupRequest(builder.build(), builder.getContentType());
+
+ List<FormDataItem> formItems =
+ Lists.newArrayList(multipartFormParser.parse(request));
+
+ assertEquals(1, formItems.size());
+ FormDataItem formItem = formItems.get(0);
+ assertTrue(formItem.isFormField());
+ assertEquals(REQUEST_FIELDNAME, formItem.getFieldName());
+ assertEquals(REQUEST_DATA, new String(formItem.get()));
+ }
+
+ public void testSingleFileItemAndRequest() throws Exception {
+ MultipartFormBuilder builder = new MultipartFormBuilder();
+ builder.addFileItem(ALBUM_IMAGE_FIELDNAME, ALBUM_IMAGE_FILENAME,
ALBUM_IMAGE_DATA,
+ ALBUM_IMAGE_TYPE);
+ builder.addFormField(REQUEST_FIELDNAME, REQUEST_DATA);
+ setupRequest(builder.build(), builder.getContentType());
+
+ List<FormDataItem> formItems =
+ Lists.newArrayList(multipartFormParser.parse(request));
+
+ assertEquals(2, formItems.size());
+ FormDataItem formItem = formItems.get(0);
+ assertFalse(formItem.isFormField());
+ assertEquals(ALBUM_IMAGE_FIELDNAME, formItem.getFieldName());
+ assertEquals(ALBUM_IMAGE_FILENAME, formItem.getName());
+ assertEquals(ALBUM_IMAGE_TYPE, formItem.getContentType());
+ assertEquals(ALBUM_IMAGE_DATA, new String(formItem.get()));
+
+ formItem = formItems.get(1);
+ assertTrue(formItem.isFormField());
+ assertEquals(REQUEST_FIELDNAME, formItem.getFieldName());
+ assertEquals(REQUEST_DATA, new String(formItem.get()));
+ }
+
+ public void testMultipleFileItemAndRequest() throws Exception {
+ MultipartFormBuilder builder = new MultipartFormBuilder();
+ builder.addFileItem(ALBUM_IMAGE_FIELDNAME, ALBUM_IMAGE_FILENAME,
ALBUM_IMAGE_DATA,
+ ALBUM_IMAGE_TYPE);
+ builder.addFormField(REQUEST_FIELDNAME, REQUEST_DATA);
+ builder.addFileItem(PROFILE_IMAGE_FIELDNAME, PROFILE_IMAGE_FILENAME,
PROFILE_IMAGE_DATA,
+ PROFILE_IMAGE_TYPE);
+ setupRequest(builder.build(), builder.getContentType());
+
+ List<FormDataItem> formItems =
+ Lists.newArrayList(multipartFormParser.parse(request));
+
+ assertEquals(3, formItems.size());
+ FormDataItem formItem = formItems.get(0);
+ assertFalse(formItem.isFormField());
+ assertEquals(ALBUM_IMAGE_FIELDNAME, formItem.getFieldName());
+ assertEquals(ALBUM_IMAGE_FILENAME, formItem.getName());
+ assertEquals(ALBUM_IMAGE_TYPE, formItem.getContentType());
+ assertEquals(ALBUM_IMAGE_DATA, new String(formItem.get()));
+
+ formItem = formItems.get(1);
+ assertTrue(formItem.isFormField());
+ assertEquals(REQUEST_FIELDNAME, formItem.getFieldName());
+ assertEquals(REQUEST_DATA, new String(formItem.get()));
+
+ formItem = formItems.get(2);
+ assertFalse(formItem.isFormField());
+ assertEquals(PROFILE_IMAGE_FIELDNAME, formItem.getFieldName());
+ assertEquals(PROFILE_IMAGE_FILENAME, formItem.getName());
+ assertEquals(PROFILE_IMAGE_TYPE, formItem.getContentType());
+ assertEquals(PROFILE_IMAGE_DATA, new String(formItem.get()));
+ }
+}
Modified:
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/SocialRequestItem.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/SocialRequestItem.java?rev=748277&r1=748276&r2=748277&view=diff
==============================================================================
---
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/SocialRequestItem.java
(original)
+++
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/SocialRequestItem.java
Thu Feb 26 19:29:46 2009
@@ -21,18 +21,19 @@
import org.apache.shindig.protocol.BaseRequestItem;
import org.apache.shindig.protocol.conversion.BeanConverter;
import org.apache.shindig.protocol.conversion.BeanJsonConverter;
+import org.apache.shindig.protocol.multipart.FormDataItem;
import org.apache.shindig.social.opensocial.spi.GroupId;
import org.apache.shindig.social.opensocial.spi.PersonService;
import org.apache.shindig.social.opensocial.spi.UserId;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
import org.json.JSONObject;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
/**
* Subclass with social specific extensions
*/
@@ -41,12 +42,14 @@
String USER_ID = "userId";
String GROUP_ID = "groupId";
- public SocialRequestItem(Map<String, String[]> parameters, SecurityToken
token, BeanConverter converter, BeanJsonConverter jsonConverter) {
+ public SocialRequestItem(Map<String, String[]> parameters,
+ SecurityToken token, BeanConverter converter, BeanJsonConverter
jsonConverter) {
super(parameters, token, converter, jsonConverter);
}
- public SocialRequestItem(JSONObject parameters, SecurityToken token,
BeanConverter converter, BeanJsonConverter jsonConverter) {
- super(parameters, token, converter, jsonConverter);
+ public SocialRequestItem(JSONObject parameters, Map<String, FormDataItem>
formItems,
+ SecurityToken token, BeanConverter converter, BeanJsonConverter
jsonConverter) {
+ super(parameters, formItems, token, converter, jsonConverter);
}
public Set<UserId> getUsers() {
Modified: incubator/shindig/trunk/pom.xml
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/pom.xml?rev=748277&r1=748276&r2=748277&view=diff
==============================================================================
--- incubator/shindig/trunk/pom.xml (original)
+++ incubator/shindig/trunk/pom.xml Thu Feb 26 19:29:46 2009
@@ -1240,6 +1240,11 @@
<version>1.3</version>
</dependency>
<dependency>
+ <groupId>commons-fileupload</groupId>
+ <artifactId>commons-fileupload</artifactId>
+ <version>1.2</version>
+ </dependency>
+ <dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20070829</version>