001/** 002 * Copyright 2015 DuraSpace, Inc. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.fcrepo.client; 017 018import static org.junit.Assert.assertEquals; 019import static java.net.URI.create; 020import static java.nio.charset.StandardCharsets.UTF_8; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertSame; 023import static org.junit.Assert.assertTrue; 024import static org.junit.Assert.fail; 025import static org.mockito.Matchers.any; 026import static org.mockito.Mockito.doThrow; 027import static org.mockito.Mockito.mock; 028import static org.mockito.Mockito.times; 029import static org.mockito.Mockito.verify; 030import static org.mockito.Mockito.when; 031 032import java.net.URI; 033import java.io.ByteArrayInputStream; 034import java.io.InputStream; 035import java.io.IOException; 036 037import com.google.common.io.ByteStreams; 038import org.apache.commons.io.output.NullOutputStream; 039import org.junit.Test; 040import org.junit.runner.RunWith; 041import org.apache.commons.io.IOUtils; 042import org.mockito.runners.MockitoJUnitRunner; 043 044/** 045 * @author ajs6f 046 */ 047@RunWith(MockitoJUnitRunner.class) 048public class FcrepoResponseTest { 049 050 @Test 051 public void testResponse() throws IOException { 052 final URI uri = create("http://localhost/path/a/b"); 053 final int status = 200; 054 final String contentType = "text/plain"; 055 final URI location = create("http://localhost/path/a/b/c"); 056 final String body = "Text response"; 057 final InputStream bodyStream = new ByteArrayInputStream(body.getBytes(UTF_8)); 058 final FcrepoResponse response = new FcrepoResponse(uri, status, contentType, location, bodyStream); 059 060 assertEquals(response.getUrl(), uri); 061 assertEquals(response.getStatusCode(), status); 062 assertEquals(response.getContentType(), contentType); 063 assertEquals(response.getLocation(), location); 064 assertEquals(IOUtils.toString(response.getBody(), UTF_8), body); 065 066 response.setUrl(create("http://example.org/path/a/b")); 067 assertEquals(response.getUrl(), create("http://example.org/path/a/b")); 068 069 response.setStatusCode(301); 070 assertEquals(response.getStatusCode(), 301); 071 072 response.setContentType("application/n-triples"); 073 assertEquals(response.getContentType(), "application/n-triples"); 074 075 response.setLocation(create("http://example.org/path/a/b/c")); 076 assertEquals(response.getLocation(), create("http://example.org/path/a/b/c")); 077 078 response.setBody(new ByteArrayInputStream( 079 "<http://example.org/book/3> <dc:title> \"Title\" .".getBytes(UTF_8))); 080 assertEquals(IOUtils.toString(response.getBody(), UTF_8), 081 "<http://example.org/book/3> <dc:title> \"Title\" ."); 082 } 083 084 /** 085 * Demonstrates that response objects are <em>not</em> {@code close()}ed by default, that the state of 086 * {@link FcrepoResponse#closed} is set appropriately when {@link FcrepoResponse#close()} is invoked under normal 087 * (i.e. no exception thrown during {@code close()}) conditions, and that {@link InputStream#close()} is not invoked 088 * repeatedly after the {@code FcrepoResponse} has been {@code close()}ed. 089 * 090 * @throws IOException if something exceptional happens 091 */ 092 @Test 093 public void testClosableReleasesResources() throws IOException { 094 final InputStream mockBody = mock(InputStream.class); 095 final FcrepoResponse underTest = new FcrepoResponse( 096 URI.create("http://localhost/foo"), 201, "text/plain", URI.create("http://localhost/bar"), mockBody); 097 098 assertFalse("FcrepoResponse objects should not be closed until close() is invoked.", underTest.isClosed()); 099 100 underTest.close(); 101 assertTrue(underTest.isClosed()); 102 verify(mockBody, times(1)).close(); 103 104 underTest.close(); 105 assertTrue(underTest.isClosed()); 106 verify(mockBody, times(1)).close(); 107 } 108 109 /** 110 * Demonstrates that if an {@code IOException} is thrown by {@link FcrepoResponse#close()}, <em>and</em> an 111 * exception is thrown inside of a client's {@code try} block, the {@code IOException} from the {@code close()} 112 * method is properly appended as a suppressed exception. 113 * 114 * @throws IOException if something exceptional happens 115 */ 116 @Test 117 public void testClosableSuppressedExceptions() throws IOException { 118 final InputStream mockBody = mock(InputStream.class); 119 final IOException notSuppressed = new IOException("Not suppressed."); 120 final IOException suppressed = new IOException("Suppressed"); 121 doThrow(suppressed).when(mockBody).close(); 122 123 try (FcrepoResponse underTest = new FcrepoResponse(URI.create("http://localhost/foo"), 201, "text/plain", 124 URI.create("http://localhost/bar"), mockBody)) { 125 assertFalse(underTest.isClosed()); 126 127 throw notSuppressed; 128 129 } catch (Exception e) { 130 assertSame(notSuppressed, e); 131 assertTrue(e.getSuppressed() != null && e.getSuppressed().length == 1); 132 assertSame(suppressed, e.getSuppressed()[0]); 133 } 134 135 verify(mockBody).close(); 136 } 137 138 /** 139 * Demonstrates a successful idiomatic usage with try-with-resources 140 * 141 * @throws FcrepoOperationFailedException if something exceptional happens 142 */ 143 @Test 144 public void testIdiomaticInvokation() throws FcrepoOperationFailedException { 145 final String content = "Hello World!"; 146 final ByteArrayInputStream entityBody = new ByteArrayInputStream(content.getBytes()); 147 final FcrepoClient client = mock(FcrepoClient.class); 148 149 when(client.get(any(URI.class), any(String.class), any(String.class))).thenReturn( 150 new FcrepoResponse(null, 200, null, null, entityBody)); 151 152 try (FcrepoResponse res = client.get(URI.create("foo"), "", "")) { 153 assertEquals(content, IOUtils.toString(res.getBody())); 154 } catch (IOException e) { 155 fail("Unexpected exception: " + e); 156 } 157 } 158 159 /** 160 * Demonstrates idiomatic exception handling with try-with-resources 161 * 162 * @throws Exception if something exceptional happens 163 */ 164 @Test 165 public void testIdiomaticInvokationThrowsException() throws Exception { 166 final InputStream mockBody = mock(InputStream.class); 167 final IOException ioe = new IOException("Mocked IOE"); 168 when(mockBody.read(any(byte[].class))).thenThrow(ioe); 169 170 final FcrepoClient client = mock(FcrepoClient.class); 171 when(client.get(any(URI.class), any(String.class), any(String.class))).thenReturn( 172 new FcrepoResponse(null, 200, null, null, mockBody)); 173 174 try (FcrepoResponse res = client.get(URI.create("foo"), "", "")) { 175 ByteStreams.copy(res.getBody(), NullOutputStream.NULL_OUTPUT_STREAM); 176 fail("Expected an IOException to be thrown."); 177 } catch (IOException e) { 178 assertSame(ioe, e); 179 } 180 181 verify(mockBody).close(); 182 } 183 184}