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 assertNotNull("Didn't find linked description!", response.getLinkHeaders("describedby").get(0)); 112 113 final FcrepoResponse getResponse = client.get(response.getLocation()).perform(); 114 final Map<String, String> contentDisp = getResponse.getContentDisposition(); 115 assertEquals(filename, contentDisp.get(CONTENT_DISPOSITION_FILENAME)); 116 117 assertEquals(mimetype, getResponse.getContentType()); 118 119 final String getContent = IOUtils.toString(getResponse.getBody(), "UTF-8"); 120 assertEquals(bodyContent, getContent); 121 } 122 123 @Test 124 public void testPostDigestMismatch() throws Exception { 125 final String bodyContent = "Hello world"; 126 final String invalidDigest = "adc83b19e793491b1c6ea0fd8b46cd9f32e592fc"; 127 128 final FcrepoResponse response = client.post(new URI(serverAddress)) 129 .body(new ByteArrayInputStream(bodyContent.getBytes()), "text/plain") 130 .digest(invalidDigest) 131 .perform(); 132 133 assertEquals("Invalid checksum was not rejected", CONFLICT.getStatusCode(), response.getStatusCode()); 134 } 135 136 @Test 137 public void testPut() throws Exception { 138 final FcrepoResponse response = create(); 139 140 assertEquals(CREATED.getStatusCode(), response.getStatusCode()); 141 assertEquals(url, response.getLocation()); 142 } 143 144 @Test 145 public void testPutEtag() throws Exception { 146 // Create object 147 final FcrepoResponse response = create(); 148 149 // Get the etag of the nearly created object 150 final String etag = response.getHeaderValue(ETAG); 151 152 // Retrieve the body of the resource so we can modify it 153 String body = getTurtle(url); 154 body += "\n<> dc:title \"some-title\""; 155 156 // Check that etag is making it through and being rejected 157 final FcrepoResponse updateResp = client.put(url) 158 .body(new ByteArrayInputStream(body.getBytes()), TEXT_TURTLE) 159 .ifMatch("\"bad-etag\"") 160 .perform(); 161 162 assertEquals(PRECONDITION_FAILED.getStatusCode(), updateResp.getStatusCode()); 163 164 // Verify that etag is retrieved and resubmitted correctly 165 final FcrepoResponse validResp = client.put(url) 166 .body(new ByteArrayInputStream(body.getBytes()), TEXT_TURTLE) 167 .ifMatch(etag) 168 .perform(); 169 assertEquals(NO_CONTENT.getStatusCode(), validResp.getStatusCode()); 170 } 171 172 @Test 173 public void testPutUnmodifiedSince() throws Exception { 174 // Create object 175 final FcrepoResponse response = create(); 176 177 // Retrieve the body of the resource so we can modify it 178 String body = getTurtle(url); 179 body += "\n<> dc:title \"some-title\""; 180 181 // Update the body the first time, which should succeed 182 final String originalModified = response.getHeaderValue(LAST_MODIFIED); 183 final FcrepoResponse matchResponse = client.put(url) 184 .body(new ByteArrayInputStream(body.getBytes()), TEXT_TURTLE) 185 .ifUnmodifiedSince(originalModified) 186 .perform(); 187 188 assertEquals(NO_CONTENT.getStatusCode(), matchResponse.getStatusCode()); 189 190 // Update the triples a second time with old timestamp 191 final FcrepoResponse mismatchResponse = client.put(url) 192 .body(new ByteArrayInputStream(body.getBytes()), TEXT_TURTLE) 193 .ifUnmodifiedSince(UNMODIFIED_DATE) 194 .perform(); 195 196 assertEquals(PRECONDITION_FAILED.getStatusCode(), mismatchResponse.getStatusCode()); 197 } 198 199 @Test 200 public void testPutLenient() throws Exception { 201 // Create object 202 final FcrepoResponse response = create(); 203 final String body = "<> <http://purl.org/dc/elements/1.1/title> \"some-title\""; 204 205 // try to update without lenient header 206 final FcrepoResponse strictResponse = client.put(url) 207 .body(new ByteArrayInputStream(body.getBytes()), TEXT_TURTLE) 208 .perform(); 209 assertEquals(CONFLICT.getStatusCode(), strictResponse.getStatusCode()); 210 211 // try again with lenient header 212 final FcrepoResponse lenientResponse = client.put(url) 213 .body(new ByteArrayInputStream(body.getBytes()), TEXT_TURTLE) 214 .preferLenient() 215 .perform(); 216 assertEquals(NO_CONTENT.getStatusCode(), lenientResponse.getStatusCode()); 217 } 218 219 @Test 220 public void testPatch() throws Exception { 221 // Create object 222 final FcrepoResponse createResp = create(); 223 final String createdEtag = createResp.getHeaderValue(ETAG); 224 225 final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes()); 226 227 // Update triples with sparql update 228 final FcrepoResponse response = client.patch(url) 229 .body(body) 230 .ifMatch(createdEtag) 231 .perform(); 232 233 final String updateEtag = response.getHeaderValue(ETAG); 234 235 assertEquals(NO_CONTENT.getStatusCode(), response.getStatusCode()); 236 assertNotEquals("Etag did not change after patch", createdEtag, updateEtag); 237 } 238 239 @Test 240 public void testPatchNoBody() throws Exception { 241 create(); 242 243 final FcrepoResponse response = client.patch(url) 244 .perform(); 245 246 assertEquals(BAD_REQUEST.getStatusCode(), response.getStatusCode()); 247 } 248 249 @Test 250 public void testDelete() throws Exception { 251 create(); 252 253 final FcrepoResponse response = client.delete(url).perform(); 254 255 assertEquals(NO_CONTENT.getStatusCode(), response.getStatusCode()); 256 257 assertEquals(GONE.getStatusCode(), client.get(url).perform().getStatusCode()); 258 } 259 260 @Test 261 public void testGet() throws Exception { 262 create(); 263 final FcrepoResponse response = client.get(url).perform(); 264 265 assertEquals(OK.getStatusCode(), response.getStatusCode()); 266 } 267 268 @Test 269 public void testGetNotFound() throws Exception { 270 final FcrepoResponse response = client.get(url).perform(); 271 272 assertEquals(NOT_FOUND.getStatusCode(), response.getStatusCode()); 273 } 274 275 @Test 276 public void testGetUnmodified() throws Exception { 277 // Check that get returns a 304 if the item hasn't changed according to last-modified/etag 278 final FcrepoResponse response = create(); 279 280 // Get tomorrows date to provide as the modified-since date 281 final String lastModified = response.getHeaderValue(LAST_MODIFIED); 282 final Date modDate = DateUtils.parseDate(lastModified); 283 final Calendar cal = Calendar.getInstance(); 284 cal.setTime(modDate); 285 cal.add(Calendar.DATE, 1); 286 287 final FcrepoResponse modResp = client.get(url) 288 .ifModifiedSince(DateUtils.formatDate(cal.getTime())) 289 .perform(); 290 291 assertEquals(NOT_MODIFIED.getStatusCode(), modResp.getStatusCode()); 292 293 final String originalEtag = response.getHeaderValue(ETAG); 294 final FcrepoResponse etagResp = client.get(url) 295 .ifNoneMatch(originalEtag) 296 .perform(); 297 assertEquals(NOT_MODIFIED.getStatusCode(), etagResp.getStatusCode()); 298 } 299 300 @Test 301 public void testGetAccept() throws Exception { 302 // Check that get returns a 304 if the item hasn't changed according to last-modified/etag 303 create(); 304 305 final FcrepoResponse response = client.get(url) 306 .accept("application/n-triples") 307 .perform(); 308 309 assertEquals("application/n-triples", response.getHeaderValue(CONTENT_TYPE)); 310 assertEquals(OK.getStatusCode(), response.getStatusCode()); 311 } 312 313 @Test 314 public void testGetPrefer() throws Exception { 315 // Check that get returns a 304 if the item hasn't changed according to last-modified/etag 316 create(); 317 318 final FcrepoResponse response = client.get(url) 319 .preferMinimal() 320 .perform(); 321 322 assertEquals(OK.getStatusCode(), response.getStatusCode()); 323 assertEquals("return=minimal", response.getHeaderValue("Preference-Applied")); 324 } 325 326 @Test 327 public void testGetRange() throws Exception { 328 // Creating a binary for retrieval 329 final String mimetype = "text/plain"; 330 final String bodyContent = "Hello world"; 331 final FcrepoResponse response = client.post(new URI(serverAddress)) 332 .body(new ByteArrayInputStream(bodyContent.getBytes()), mimetype) 333 .perform(); 334 335 final URI url = response.getLocation(); 336 337 // Get the content of the object after the first 6 bytes 338 final FcrepoResponse rangeResp = client.get(url) 339 .range(6L, null) 340 .perform(); 341 342 final String content = IOUtils.toString(rangeResp.getBody(), "UTF-8"); 343 assertEquals("Body did not contain correct range of original content", "world", content); 344 assertEquals(PARTIAL_CONTENT.getStatusCode(), rangeResp.getStatusCode()); 345 } 346 347 @Test 348 public void testGetDisableRedirects() throws Exception { 349 // Creating a binary with external content for retrieval 350 final String mimetype = "message/external-body; access-type=URL; URL=\"http://www.example.com/file\""; 351 final FcrepoResponse response = client.post(new URI(serverAddress)) 352 .body(new ByteArrayInputStream(new byte[]{}), mimetype) 353 .perform(); 354 355 final URI url = response.getLocation(); 356 357 // Make sure the response is the redirect itself, not the URL being redirected to 358 final FcrepoResponse getResponse = client.get(url).disableRedirects().perform(); 359 assertEquals(307, getResponse.getStatusCode()); 360 assertEquals(url, getResponse.getUrl()); 361 assertEquals(URI.create("http://www.example.com/file"), getResponse.getLocation()); 362 } 363 364 @Test 365 public void testHead() throws Exception { 366 final FcrepoResponse response = create(); 367 final FcrepoResponse headResp = client.head(url).perform(); 368 369 assertEquals(OK.getStatusCode(), headResp.getStatusCode()); 370 assertEquals(response.getHeaderValue(ETAG), headResp.getHeaderValue(ETAG)); 371 assertNotNull(headResp.getHeaderValue("Allow")); 372 } 373 374 @Test 375 public void testOptions() throws Exception { 376 create(); 377 final FcrepoResponse headResp = client.options(url).perform(); 378 379 assertEquals(OK.getStatusCode(), headResp.getStatusCode()); 380 assertNotNull(headResp.getHeaderValue("Allow")); 381 assertNotNull(headResp.getHeaderValue("Accept-Post")); 382 assertNotNull(headResp.getHeaderValue("Accept-Patch")); 383 } 384 385 @Test 386 public void testMove() throws Exception { 387 create(); 388 389 final URI destUrl = new URI(url.toString() + "_dest"); 390 final FcrepoResponse moveResp = client.move(url, destUrl).perform(); 391 assertEquals(CREATED.getStatusCode(), moveResp.getStatusCode()); 392 393 assertEquals("Object still at original url", 394 GONE.getStatusCode(), client.get(url).perform().getStatusCode()); 395 396 assertEquals("Object not at expected new url", 397 OK.getStatusCode(), client.get(destUrl).perform().getStatusCode()); 398 } 399 400 @Test 401 public void testCopy() throws Exception { 402 create(); 403 404 // Add something identifiable to the record 405 final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes()); 406 client.patch(url).body(body).perform(); 407 408 final URI destUrl = new URI(url.toString() + "_dest"); 409 final FcrepoResponse copyResp = client.copy(url, destUrl).perform(); 410 assertEquals(CREATED.getStatusCode(), copyResp.getStatusCode()); 411 412 final FcrepoResponse originalResp = client.get(url).perform(); 413 final String originalContent = IOUtils.toString(originalResp.getBody(), "UTF-8"); 414 assertTrue(originalContent.contains("Foo")); 415 416 final FcrepoResponse destResp = client.get(destUrl).perform(); 417 final String destContent = IOUtils.toString(destResp.getBody(), "UTF-8"); 418 assertTrue(destContent.contains("Foo")); 419 } 420 421 private FcrepoResponse create() throws FcrepoOperationFailedException { 422 return client.put(url).perform(); 423 } 424 425 private String getTurtle(final URI url) throws Exception { 426 final FcrepoResponse getResponse = client.get(url) 427 .accept("text/turtle") 428 .perform(); 429 return IOUtils.toString(getResponse.getBody(), "UTF-8"); 430 } 431}