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 */ 016 017package org.fcrepo.client.integration; 018 019import static javax.ws.rs.core.Response.Status.BAD_REQUEST; 020import static javax.ws.rs.core.Response.Status.CONFLICT; 021import static javax.ws.rs.core.Response.Status.CREATED; 022import static javax.ws.rs.core.Response.Status.GONE; 023import static javax.ws.rs.core.Response.Status.NOT_FOUND; 024import static javax.ws.rs.core.Response.Status.NOT_MODIFIED; 025import static javax.ws.rs.core.Response.Status.NO_CONTENT; 026import static javax.ws.rs.core.Response.Status.OK; 027import static javax.ws.rs.core.Response.Status.PARTIAL_CONTENT; 028import static javax.ws.rs.core.Response.Status.PRECONDITION_FAILED; 029import static org.fcrepo.client.FedoraHeaderConstants.CONTENT_DISPOSITION_FILENAME; 030import static org.fcrepo.client.FedoraHeaderConstants.CONTENT_TYPE; 031import static org.fcrepo.client.FedoraHeaderConstants.ETAG; 032import static org.fcrepo.client.FedoraHeaderConstants.LAST_MODIFIED; 033import static org.fcrepo.client.TestUtils.TEXT_TURTLE; 034import static org.fcrepo.client.TestUtils.sparqlUpdate; 035import static org.junit.Assert.assertEquals; 036import static org.junit.Assert.assertNotEquals; 037import static org.junit.Assert.assertNotNull; 038import static org.junit.Assert.assertTrue; 039 040import java.io.ByteArrayInputStream; 041import java.io.InputStream; 042import java.net.URI; 043import java.util.Calendar; 044import java.util.Date; 045import java.util.Map; 046 047import org.apache.commons.io.IOUtils; 048import org.apache.http.client.utils.DateUtils; 049import org.fcrepo.client.FcrepoClient; 050import org.fcrepo.client.FcrepoOperationFailedException; 051import org.fcrepo.client.FcrepoResponse; 052import org.jgroups.util.UUID; 053import org.junit.Before; 054import org.junit.Test; 055import org.junit.runner.RunWith; 056import org.springframework.test.context.ContextConfiguration; 057import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 058 059/** 060 * @author bbpennel 061 */ 062@RunWith(SpringJUnit4ClassRunner.class) 063@ContextConfiguration("/spring-test/test-container.xml") 064public class FcrepoClientIT extends AbstractResourceIT { 065 066 protected URI url; 067 068 final private String UNMODIFIED_DATE = "Mon, 1 Jan 2001 00:00:00 GMT"; 069 070 public FcrepoClientIT() throws Exception { 071 super(); 072 073 client = FcrepoClient.client() 074 .credentials("fedoraAdmin", "password") 075 .authScope("localhost") 076 .build(); 077 } 078 079 @Before 080 public void before() { 081 url = URI.create(serverAddress + UUID.randomUUID().toString()); 082 } 083 084 @Test 085 public void testPost() throws Exception { 086 final FcrepoResponse response = client.post(new URI(serverAddress)) 087 .perform(); 088 089 assertEquals(CREATED.getStatusCode(), response.getStatusCode()); 090 } 091 092 @Test 093 public void testPostBinary() throws Exception { 094 final String slug = "hello1"; 095 final String filename = "hello.txt"; 096 final String mimetype = "text/plain"; 097 final String bodyContent = "Hello world"; 098 final FcrepoResponse response = client.post(new URI(serverAddress)) 099 .body(new ByteArrayInputStream(bodyContent.getBytes()), mimetype) 100 .filename(filename) 101 .slug(slug) 102 .perform(); 103 104 final String content = IOUtils.toString(response.getBody(), "UTF-8"); 105 final int status = response.getStatusCode(); 106 107 assertEquals("Didn't get a CREATED response! Got content:\n" + content, 108 CREATED.getStatusCode(), status); 109 assertEquals("Location did not match slug", serverAddress + slug, response.getLocation().toString()); 110 111 final FcrepoResponse getResponse = client.get(response.getLocation()).perform(); 112 final Map<String, String> contentDisp = getResponse.getContentDisposition(); 113 assertEquals(filename, contentDisp.get(CONTENT_DISPOSITION_FILENAME)); 114 115 assertEquals(mimetype, getResponse.getContentType()); 116 117 final String getContent = IOUtils.toString(getResponse.getBody(), "UTF-8"); 118 assertEquals(bodyContent, getContent); 119 } 120 121 @Test 122 public void testPostDigestMismatch() throws Exception { 123 final String bodyContent = "Hello world"; 124 final String invalidDigest = "adc83b19e793491b1c6ea0fd8b46cd9f32e592fc"; 125 126 final FcrepoResponse response = client.post(new URI(serverAddress)) 127 .body(new ByteArrayInputStream(bodyContent.getBytes()), "text/plain") 128 .digest(invalidDigest) 129 .perform(); 130 131 assertEquals("Invalid checksum was not rejected", CONFLICT.getStatusCode(), response.getStatusCode()); 132 } 133 134 @Test 135 public void testPut() throws Exception { 136 final FcrepoResponse response = create(); 137 138 assertEquals(CREATED.getStatusCode(), response.getStatusCode()); 139 assertEquals(url, response.getLocation()); 140 } 141 142 @Test 143 public void testPutEtag() throws Exception { 144 // Create object 145 final FcrepoResponse response = create(); 146 147 // Get the etag of the nearly created object 148 final String etag = response.getHeaderValue(ETAG); 149 150 // Retrieve the body of the resource so we can modify it 151 String body = getTurtle(url); 152 body += "\n<> dc:title \"some-title\""; 153 154 // Check that etag is making it through and being rejected 155 final FcrepoResponse updateResp = client.put(url) 156 .body(new ByteArrayInputStream(body.getBytes()), TEXT_TURTLE) 157 .ifMatch("\"bad-etag\"") 158 .perform(); 159 160 assertEquals(PRECONDITION_FAILED.getStatusCode(), updateResp.getStatusCode()); 161 162 // Verify that etag is retrieved and resubmitted correctly 163 final FcrepoResponse validResp = client.put(url) 164 .body(new ByteArrayInputStream(body.getBytes()), TEXT_TURTLE) 165 .ifMatch(etag) 166 .perform(); 167 assertEquals(NO_CONTENT.getStatusCode(), validResp.getStatusCode()); 168 } 169 170 @Test 171 public void testPutUnmodifiedSince() throws Exception { 172 // Create object 173 final FcrepoResponse response = create(); 174 175 // Retrieve the body of the resource so we can modify it 176 String body = getTurtle(url); 177 body += "\n<> dc:title \"some-title\""; 178 179 // Update the body the first time, which should succeed 180 final String originalModified = response.getHeaderValue(LAST_MODIFIED); 181 final FcrepoResponse matchResponse = client.put(url) 182 .body(new ByteArrayInputStream(body.getBytes()), TEXT_TURTLE) 183 .ifUnmodifiedSince(originalModified) 184 .perform(); 185 186 assertEquals(NO_CONTENT.getStatusCode(), matchResponse.getStatusCode()); 187 188 // Update the triples a second time with old timestamp 189 final FcrepoResponse mismatchResponse = client.put(url) 190 .body(new ByteArrayInputStream(body.getBytes()), TEXT_TURTLE) 191 .ifUnmodifiedSince(UNMODIFIED_DATE) 192 .perform(); 193 194 assertEquals(PRECONDITION_FAILED.getStatusCode(), mismatchResponse.getStatusCode()); 195 } 196 197 @Test 198 public void testPatch() throws Exception { 199 // Create object 200 final FcrepoResponse createResp = create(); 201 final String createdEtag = createResp.getHeaderValue(ETAG); 202 203 final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes()); 204 205 // Update triples with sparql update 206 final FcrepoResponse response = client.patch(url) 207 .body(body) 208 .ifMatch(createdEtag) 209 .perform(); 210 211 final String updateEtag = response.getHeaderValue(ETAG); 212 213 assertEquals(NO_CONTENT.getStatusCode(), response.getStatusCode()); 214 assertNotEquals("Etag did not change after patch", createdEtag, updateEtag); 215 } 216 217 @Test 218 public void testPatchNoBody() throws Exception { 219 create(); 220 221 final FcrepoResponse response = client.patch(url) 222 .perform(); 223 224 assertEquals(BAD_REQUEST.getStatusCode(), response.getStatusCode()); 225 } 226 227 @Test 228 public void testDelete() throws Exception { 229 create(); 230 231 final FcrepoResponse response = client.delete(url).perform(); 232 233 assertEquals(NO_CONTENT.getStatusCode(), response.getStatusCode()); 234 235 assertEquals(GONE.getStatusCode(), client.get(url).perform().getStatusCode()); 236 } 237 238 @Test 239 public void testGet() throws Exception { 240 create(); 241 final FcrepoResponse response = client.get(url).perform(); 242 243 assertEquals(OK.getStatusCode(), response.getStatusCode()); 244 } 245 246 @Test 247 public void testGetNotFound() throws Exception { 248 final FcrepoResponse response = client.get(url).perform(); 249 250 assertEquals(NOT_FOUND.getStatusCode(), response.getStatusCode()); 251 } 252 253 @Test 254 public void testGetUnmodified() throws Exception { 255 // Check that get returns a 304 if the item hasn't changed according to last-modified/etag 256 final FcrepoResponse response = create(); 257 258 // Get tomorrows date to provide as the modified-since date 259 final String lastModified = response.getHeaderValue(LAST_MODIFIED); 260 final Date modDate = DateUtils.parseDate(lastModified); 261 final Calendar cal = Calendar.getInstance(); 262 cal.setTime(modDate); 263 cal.add(Calendar.DATE, 1); 264 265 final FcrepoResponse modResp = client.get(url) 266 .ifModifiedSince(DateUtils.formatDate(cal.getTime())) 267 .perform(); 268 269 assertEquals(NOT_MODIFIED.getStatusCode(), modResp.getStatusCode()); 270 271 final String originalEtag = response.getHeaderValue(ETAG); 272 final FcrepoResponse etagResp = client.get(url) 273 .ifNoneMatch(originalEtag) 274 .perform(); 275 assertEquals(NOT_MODIFIED.getStatusCode(), etagResp.getStatusCode()); 276 } 277 278 @Test 279 public void testGetAccept() throws Exception { 280 // Check that get returns a 304 if the item hasn't changed according to last-modified/etag 281 create(); 282 283 final FcrepoResponse response = client.get(url) 284 .accept("application/n-triples") 285 .perform(); 286 287 assertEquals("application/n-triples", response.getHeaderValue(CONTENT_TYPE)); 288 assertEquals(OK.getStatusCode(), response.getStatusCode()); 289 } 290 291 @Test 292 public void testGetPrefer() throws Exception { 293 // Check that get returns a 304 if the item hasn't changed according to last-modified/etag 294 create(); 295 296 final FcrepoResponse response = client.get(url) 297 .preferMinimal() 298 .perform(); 299 300 assertEquals(OK.getStatusCode(), response.getStatusCode()); 301 assertEquals("return=minimal", response.getHeaderValue("Preference-Applied")); 302 } 303 304 @Test 305 public void testGetRange() throws Exception { 306 // Creating a binary for retrieval 307 final String mimetype = "text/plain"; 308 final String bodyContent = "Hello world"; 309 final FcrepoResponse response = client.post(new URI(serverAddress)) 310 .body(new ByteArrayInputStream(bodyContent.getBytes()), mimetype) 311 .perform(); 312 313 final URI url = response.getLocation(); 314 315 // Get the content of the object after the first 6 bytes 316 final FcrepoResponse rangeResp = client.get(url) 317 .range(6L, null) 318 .perform(); 319 320 final String content = IOUtils.toString(rangeResp.getBody(), "UTF-8"); 321 assertEquals("Body did not contain correct range of original content", "world", content); 322 assertEquals(PARTIAL_CONTENT.getStatusCode(), rangeResp.getStatusCode()); 323 } 324 325 @Test 326 public void testHead() throws Exception { 327 final FcrepoResponse response = create(); 328 final FcrepoResponse headResp = client.head(url).perform(); 329 330 assertEquals(OK.getStatusCode(), headResp.getStatusCode()); 331 assertEquals(response.getHeaderValue(ETAG), headResp.getHeaderValue(ETAG)); 332 assertNotNull(headResp.getHeaderValue("Allow")); 333 } 334 335 @Test 336 public void testOptions() throws Exception { 337 create(); 338 final FcrepoResponse headResp = client.options(url).perform(); 339 340 assertEquals(OK.getStatusCode(), headResp.getStatusCode()); 341 assertNotNull(headResp.getHeaderValue("Allow")); 342 assertNotNull(headResp.getHeaderValue("Accept-Post")); 343 assertNotNull(headResp.getHeaderValue("Accept-Patch")); 344 } 345 346 @Test 347 public void testMove() throws Exception { 348 create(); 349 350 final URI destUrl = new URI(url.toString() + "_dest"); 351 final FcrepoResponse moveResp = client.move(url, destUrl).perform(); 352 assertEquals(CREATED.getStatusCode(), moveResp.getStatusCode()); 353 354 assertEquals("Object still at original url", 355 GONE.getStatusCode(), client.get(url).perform().getStatusCode()); 356 357 assertEquals("Object not at expected new url", 358 OK.getStatusCode(), client.get(destUrl).perform().getStatusCode()); 359 } 360 361 @Test 362 public void testCopy() throws Exception { 363 create(); 364 365 // Add something identifiable to the record 366 final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes()); 367 client.patch(url).body(body).perform(); 368 369 final URI destUrl = new URI(url.toString() + "_dest"); 370 final FcrepoResponse copyResp = client.copy(url, destUrl).perform(); 371 assertEquals(CREATED.getStatusCode(), copyResp.getStatusCode()); 372 373 final FcrepoResponse originalResp = client.get(url).perform(); 374 final String originalContent = IOUtils.toString(originalResp.getBody(), "UTF-8"); 375 assertTrue(originalContent.contains("Foo")); 376 377 final FcrepoResponse destResp = client.get(destUrl).perform(); 378 final String destContent = IOUtils.toString(destResp.getBody(), "UTF-8"); 379 assertTrue(destContent.contains("Foo")); 380 } 381 382 private FcrepoResponse create() throws FcrepoOperationFailedException { 383 return client.put(url).perform(); 384 } 385 386 private String getTurtle(final URI url) throws Exception { 387 final FcrepoResponse getResponse = client.get(url) 388 .accept("text/turtle") 389 .perform(); 390 return IOUtils.toString(getResponse.getBody(), "UTF-8"); 391 } 392}