package eu.europeana.cloud.service.mcs.rest; import com.google.common.collect.ImmutableMap; import eu.europeana.cloud.common.model.File; import eu.europeana.cloud.common.web.ParamConstants; import eu.europeana.cloud.service.mcs.ApplicationContextUtils; import eu.europeana.cloud.service.mcs.RecordService; import eu.europeana.cloud.test.CassandraTestRunner; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Date; import javax.ws.rs.Path; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Application; import javax.ws.rs.core.Response; import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.glassfish.jersey.test.JerseyTest; import org.junit.After; import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; import org.mockito.Mockito; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.reset; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.springframework.context.ApplicationContext; /** * This tests checks if content is streamed (not put entirely into memory) when downloading file. */ @RunWith(CassandraTestRunner.class) public class HugeFileResourceDownloadIT extends JerseyTest { private static RecordService recordService; private static final int HUGE_FILE_SIZE = 1 << 30; @Before public void mockUp() throws Exception { ApplicationContext applicationContext = ApplicationContextUtils.getApplicationContext(); recordService = applicationContext.getBean(RecordService.class); } @After public void cleanUp() throws Exception { reset(recordService); } @Override public Application configure() { return new JerseyConfig().property("contextConfigLocation", "classpath:spiedPersistentServicesTestContext.xml"); } @Override protected void configureClient(ClientConfig config) { config.register(MultiPartFeature.class); } @Test public void shouldHandleHugeFile() throws Exception { // given representation with file in service String globalId = "globalId", schema = "schema", version = "v1"; MockGetContentMethod mockGetContent = new MockGetContentMethod(HUGE_FILE_SIZE); File file = new File("fileName", "text/plain", "md5", new Date().toString(), HUGE_FILE_SIZE, null); // mock answers: doAnswer(mockGetContent).when(recordService).getContent(anyString(), anyString(), anyString(), anyString(), anyLong(), anyLong(), any(OutputStream.class)); Mockito.doReturn(file).when(recordService).getFile(globalId, schema, version, file.getFileName()); // when we download mocked content of resource WebTarget webTarget = target(FileResource.class.getAnnotation(Path.class).value()) // .resolveTemplates(ImmutableMap.<String, Object> of( // ParamConstants.P_CLOUDID, globalId, // ParamConstants.P_REPRESENTATIONNAME, schema, // ParamConstants.P_VER, version, // ParamConstants.P_FILENAME, file.getFileName())); Response response = webTarget.request().get(); assertEquals("Unsuccessful request", Response.Status.Family.SUCCESSFUL, response.getStatusInfo().getFamily()); // then - we should be able to get full content and the content should have expected size InputStream responseStream = response.readEntity(InputStream.class); int totalBytesInResponse = getBytesCount(responseStream); assertEquals("Wrong size of read content", HUGE_FILE_SIZE, totalBytesInResponse); } private int getBytesCount(InputStream is) throws IOException { int totalBytes = 0; int nRead; byte[] data = new byte[16384]; while ((nRead = is.read(data, 0, data.length)) != -1) { totalBytes += nRead; } return totalBytes; } /** * Mock answer for * {@link ContentService#getContent(eu.europeana.cloud.common.model.Representation, eu.europeana.cloud.common.model.File, long, long, java.io.OutputStream) * getContent} method. */ static class MockGetContentMethod implements Answer<Object> { final int totalBytes; public MockGetContentMethod(int totalBytes) { this.totalBytes = totalBytes; } @Override public Object answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); OutputStream os = (OutputStream) args[6]; writeBytes(os); return null; } private void writeBytes(OutputStream os) throws IOException { for (int i = 0; i < totalBytes; i++) { os.write(1); } } } }