Java tutorial
/* * This file is a component of thundr, a software library from 3wks. * Read more: http://3wks.github.io/thundr/ * Copyright (C) 2014 3wks, <thundr@3wks.com.au> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.threewks.thundr.bind.http; import static com.atomicleopard.expressive.Expressive.list; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.*; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItemHeaders; import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileItemStream; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import com.threewks.thundr.bind.BindException; import com.threewks.thundr.bind.parameter.ParameterBinderRegistry; import com.threewks.thundr.http.ContentType; import com.threewks.thundr.http.MultipartFile; import com.threewks.thundr.introspection.ParameterDescription; import com.threewks.thundr.test.TestSupport; import com.threewks.thundr.test.mock.servlet.MockHttpServletRequest; import com.threewks.thundr.test.mock.servlet.MockHttpServletResponse; import com.threewks.thundr.transformer.TransformerManager; import com.threewks.thundr.util.Streams; public class MultipartHttpBinderTest { @Rule public ExpectedException thrown = ExpectedException.none(); private MultipartHttpBinder binder; private MockHttpServletRequest request = new MockHttpServletRequest() .contentType(ContentType.MultipartFormData); private MockHttpServletResponse response = new MockHttpServletResponse(); private Map<String, String> pathVariables; private Map<ParameterDescription, Object> parameterDescriptions; private ArrayList<FileItemStream> multipartData; private ParameterBinderRegistry parameterBinderRegistry; @Before public void before() throws FileUploadException, IOException { parameterBinderRegistry = new ParameterBinderRegistry(TransformerManager.createWithDefaults()); ParameterBinderRegistry.addDefaultBinders(parameterBinderRegistry); binder = new MultipartHttpBinder(parameterBinderRegistry); parameterDescriptions = new LinkedHashMap<ParameterDescription, Object>(); pathVariables = new HashMap<String, String>(); multipartData = new ArrayList<FileItemStream>(); ServletFileUpload mockUpload = mock(ServletFileUpload.class); when(mockUpload.getItemIterator(request)).thenAnswer(new Answer<FileItemIterator>() { @Override public FileItemIterator answer(InvocationOnMock invocation) throws Throwable { return new FileItemIterator() { Iterator<FileItemStream> iterator = multipartData.iterator(); @Override public FileItemStream next() throws FileUploadException, IOException { return iterator.next(); } @Override public boolean hasNext() throws FileUploadException, IOException { return iterator.hasNext(); } }; } }); TestSupport.setField(binder, "upload", mockUpload); } @Test public void shouldOnlyBindMultipartContent() { request.contentType(ContentType.ApplicationFormUrlEncoded); ParameterDescription field1 = new ParameterDescription("field1", String.class); ParameterDescription field2 = new ParameterDescription("field2", String.class); addFormField("field1", "value1"); addFormField("field2", "value2"); addFormField("field3", "value3"); parameterDescriptions.put(field1, null); parameterDescriptions.put(field2, null); binder.bindAll(parameterDescriptions, request, response, pathVariables); assertThat(parameterDescriptions.get(field1), is(nullValue())); assertThat(parameterDescriptions.get(field2), is(nullValue())); request.contentType(ContentType.MultipartFormData); binder.bindAll(parameterDescriptions, request, response, pathVariables); assertThat(parameterDescriptions.get(field1), is(notNullValue())); assertThat(parameterDescriptions.get(field2), is(notNullValue())); } @Test public void shouldBindFormFieldsByDelegatingToHttpBinder() { ParameterDescription field1 = new ParameterDescription("field1", String.class); ParameterDescription field2 = new ParameterDescription("field2", String.class); addFormField("field1", "value1"); addFormField("field2", "value2"); addFormField("field3", "value3"); parameterDescriptions.put(field1, null); parameterDescriptions.put(field2, null); binder.bindAll(parameterDescriptions, request, response, pathVariables); assertThat(parameterDescriptions.get(field1), is((Object) "value1")); assertThat(parameterDescriptions.get(field2), is((Object) "value2")); assertThat(parameterDescriptions.size(), is(2)); } @Test public void shouldBindByteArrayFromFileData() { ParameterDescription field1 = new ParameterDescription("field1", String.class); ParameterDescription field2 = new ParameterDescription("field2", String.class); ParameterDescription data = new ParameterDescription("data", byte[].class); addFormField("field1", "value1"); addFormField("field2", "value2"); addFileField("data", new byte[] { 1, 2, 3 }); parameterDescriptions.put(field1, null); parameterDescriptions.put(field2, null); parameterDescriptions.put(data, null); binder.bindAll(parameterDescriptions, request, response, pathVariables); assertThat(parameterDescriptions.get(field1), is((Object) "value1")); assertThat(parameterDescriptions.get(field2), is((Object) "value2")); assertThat(parameterDescriptions.get(data), is((Object) new byte[] { 1, 2, 3 })); assertThat(parameterDescriptions.size(), is(3)); } @Test public void shouldBindInputStreamFromFileData() { ParameterDescription field1 = new ParameterDescription("field1", String.class); ParameterDescription field2 = new ParameterDescription("field2", String.class); ParameterDescription data = new ParameterDescription("data", InputStream.class); addFormField("field1", "value1"); addFormField("field2", "value2"); addFileField("data", new byte[] { 1, 2, 3 }); parameterDescriptions.put(field1, null); parameterDescriptions.put(field2, null); parameterDescriptions.put(data, null); binder.bindAll(parameterDescriptions, request, response, pathVariables); assertThat(parameterDescriptions.get(field1), is((Object) "value1")); assertThat(parameterDescriptions.get(field2), is((Object) "value2")); Object actualData = parameterDescriptions.get(data); assertThat(actualData, is(notNullValue())); assertThat(actualData instanceof InputStream, is(true)); byte[] underlyingData = Streams.readBytes((InputStream) actualData); assertThat(underlyingData, is(new byte[] { 1, 2, 3 })); assertThat(parameterDescriptions.size(), is(3)); } @SuppressWarnings("unchecked") @Test public void shouldNotBindIfAllParametersAreAlreadyBound() { ParameterDescription req = new ParameterDescription("req", HttpServletRequest.class); ParameterDescription resp = new ParameterDescription("resp", HttpServletResponse.class); ParameterDescription cookie = new ParameterDescription("cookie", String.class); addFormField("field1", "value1"); addFormField("field2", "value2"); addFileField("data", new byte[] { 1, 2, 3 }); parameterDescriptions.put(req, new MockHttpServletRequest()); parameterDescriptions.put(resp, new MockHttpServletResponse()); parameterDescriptions.put(cookie, "cookie-value"); binder = spy(binder); binder.bindAll(parameterDescriptions, request, response, pathVariables); verify(binder, times(0)).extractParameters(Mockito.any(HttpServletRequest.class), Mockito.anyMap(), Mockito.anyMapOf(String.class, MultipartFile.class)); assertThat(binder.shouldTryToBind(parameterDescriptions), is(false)); } @SuppressWarnings("unchecked") @Test public void shouldNotBindIfNoParametersArePresent() { addFormField("field1", "value1"); addFormField("field2", "value2"); addFileField("data", new byte[] { 1, 2, 3 }); binder = spy(binder); binder.bindAll(parameterDescriptions, request, response, pathVariables); verify(binder, times(0)).extractParameters(Mockito.any(HttpServletRequest.class), Mockito.anyMap(), Mockito.anyMapOf(String.class, MultipartFile.class)); assertThat(binder.shouldTryToBind(parameterDescriptions), is(false)); } @Test public void shouldFindRequestAsMultipart() { assertThat( ContentType.matchesAny("multipart/form-data; boundary=127.0.0.1.1002.16893.1359095066.582.122048", list(ContentType.MultipartFormData)), is(true)); } @Test public void shouldThrowBindExceptionWhenFailsToBind() { thrown.expect(BindException.class); thrown.expectMessage("Failed to bind multipart form data: "); ParameterDescription data = new ParameterDescription("data", byte[].class); addFileField("data", null); parameterDescriptions.put(data, null); binder.bindAll(parameterDescriptions, request, response, pathVariables); } private void addFormField(final String name, final String value) { multipartData.add(new FileItemStream() { @Override public void setHeaders(FileItemHeaders headers) { } @Override public FileItemHeaders getHeaders() { return null; } @Override public InputStream openStream() throws IOException { return new ByteArrayInputStream(value.getBytes("UTF-8")); } @Override public boolean isFormField() { return true; } @Override public String getName() { return name; } @Override public String getFieldName() { return name; } @Override public String getContentType() { return null; } }); } private void addFileField(final String name, final byte[] data) { multipartData.add(new FileItemStream() { @Override public void setHeaders(FileItemHeaders headers) { } @Override public FileItemHeaders getHeaders() { return null; } @Override public InputStream openStream() throws IOException { return new ByteArrayInputStream(data); } @Override public boolean isFormField() { return false; } @Override public String getName() { return name; } @Override public String getFieldName() { return name; } @Override public String getContentType() { return null; } }); } }