package nl.knaw.huygens.alexandria.client;
/*
* #%L
* alexandria-java-client
* =======
* Copyright (C) 2015 - 2017 Huygens ING (KNAW)
* =======
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import static nl.knaw.huygens.alexandria.api.ApiConstants.HEADER_AUTH;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.client.ChunkedInput;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableMap;
import nl.knaw.huygens.Log;
import nl.knaw.huygens.alexandria.api.EndpointPaths;
import nl.knaw.huygens.alexandria.api.model.iiif.IIIFAnnotationList;
import nl.knaw.huygens.alexandria.api.model.w3c.WebAnnotationPrototype;
import nl.knaw.huygens.alexandria.api.w3c.WebAnnotationConstants;
public class IIIFPerformanceTest extends AlexandriaTestWithTestServer {
private static OptimisticAlexandriaClient client;
@BeforeClass
public static void startClient() {
client = new OptimisticAlexandriaClient("http://localhost:2016/");
}
@AfterClass
public static void stopClient() {
client.close();
}
@Before
public void before() {
client.setAuthKey(AUTHKEY);
client.setAutoConfirm(true);
}
static final String authHeader = "SimpleAuth " + AUTHKEY;
// @Test
public void testBatchAnnotationUploadOld() {
int num = 100;
IIIFAnnotationList list = prepareList(num);
Stopwatch sw = Stopwatch.createStarted();
WebTarget rootTarget = client.getRootTarget();
Response response = rootTarget.path(EndpointPaths.IIIF)//
.path("identifier")//
.path("list")//
.path("name")//
.path("oldway")//
.request()//
.accept(WebAnnotationConstants.JSONLD_MEDIATYPE)//
.header(HEADER_AUTH, authHeader)//
.post(Entity.entity(list, WebAnnotationConstants.JSONLD_MEDIATYPE));
sw.stop();
long elapsed = sw.elapsed(TimeUnit.MILLISECONDS);
IIIFAnnotationList responseList = response.readEntity(IIIFAnnotationList.class);
Log.info("NON-STREAMING: Uploading a batch of {} annotations took {} milliseconds.\nresponse: {}", num, elapsed, response);
assertThat(responseList.getResources()).hasSize(num);
assertThat(responseList.getResources().get(0).getCreated()).isNotBlank();
assertThat(responseList.getResources().get(0).getVariablePart().get("@id").toString()).startsWith("http");
}
@Test
public void testBatchAnnotationUploadStreaming() throws IOException {
int num = 100; // TODO: find out why num=1000 leads to TimeOutException for this test only
IIIFAnnotationList list = prepareList(num);
String json = new ObjectMapper().writeValueAsString(list);
Stopwatch sw = Stopwatch.createStarted();
WebTarget rootTarget = client.getRootTarget();
Response response = rootTarget.path(EndpointPaths.IIIF)//
.path("identifier")//
.path("list")//
.path("name")//
.request()//
.accept(WebAnnotationConstants.JSONLD_MEDIATYPE)//
.header(HEADER_AUTH, authHeader)//
.post(Entity.entity(json, WebAnnotationConstants.JSONLD_MEDIATYPE));
sw.stop();
IIIFAnnotationList responseList = response.readEntity(IIIFAnnotationList.class);
long elapsed = sw.elapsed(TimeUnit.MILLISECONDS);
Log.info("STREAMING: Uploading a batch of {} annotations took {} milliseconds.\nresponse: {}", num, elapsed, response);
assertThat(responseList.getResources()).hasSize(num);
assertThat(responseList.getResources().get(0).getCreated()).isNotBlank();
assertThat(responseList.getResources().get(0).getVariablePart().get("@id").toString()).startsWith("http");
}
@Test
public void testBatchAnnotationUploadChunked() throws IOException {
int num = 1000;
IIIFAnnotationList list = prepareList(num);
String json = new ObjectMapper().writeValueAsString(list);
Stopwatch sw = Stopwatch.createStarted();
WebTarget rootTarget = client.getRootTarget();
Response response = rootTarget.path(EndpointPaths.IIIF)//
.path("identifier")//
.path("list")//
.path("name")//
.path("chunked")//
.request()//
.accept(WebAnnotationConstants.JSONLD_MEDIATYPE)//
.header(HEADER_AUTH, authHeader)//
.post(Entity.entity(json, WebAnnotationConstants.JSONLD_MEDIATYPE));
final ChunkedInput<String> chunkedInput = response.readEntity(new GenericType<ChunkedInput<String>>() {
});
StringBuilder jsonBuilder = new StringBuilder();
String chunk;
while ((chunk = chunkedInput.read()) != null) {
System.out.println("<" + chunk);
jsonBuilder.append(chunk);
}
sw.stop();
String jsonOut = jsonBuilder.toString();
Log.info("jsonOut={}", jsonOut);
IIIFAnnotationList responseList = new ObjectMapper().readValue(jsonOut, IIIFAnnotationList.class);
long elapsed = sw.elapsed(TimeUnit.MILLISECONDS);
Log.info("CHUNKED: Uploading a batch of {} annotations took {} milliseconds.\nresponse: {}", num, elapsed, response);
assertThat(responseList.getResources()).hasSize(num);
assertThat(responseList.getResources().get(0).getCreated()).isNotBlank();
assertThat(responseList.getResources().get(0).getVariablePart().get("@id").toString()).startsWith("http");
}
private IIIFAnnotationList prepareList(int num) {
IIIFAnnotationList list = new IIIFAnnotationList();
list.setContext("http://iiif.io/api/presentation/2/context.json");
list.putKeyValue("@type", "sc:AnnotationList");
List<WebAnnotationPrototype> resources = new ArrayList<>(num);
for (int i = 0; i < num; i++) {
WebAnnotationPrototype webannotation = new WebAnnotationPrototype();
webannotation.putKeyValue("@type", "sc:Annotation");
String[] motivation = new String[]{"oa:tagging", "oa:commenting"};
webannotation.putKeyValue("motivation", motivation);
List<Map<String, String>> resourceList = new ArrayList<>(2);
resourceList.add(ImmutableMap.<String, String>of(//
"@type", "oa:Tag", //
"chars", "Square"//
));
resourceList.add(ImmutableMap.<String, String>of(//
"@type", "dctypes:Text", //
"format", "text/html", //
"chars", "<p>text " + i + "</p>"//
));
webannotation.putKeyValue("resource", resourceList);
webannotation.putKeyValue("on", "https://purl.stanford.edu/wh234bz9013/iiif/canvas-0#xywh=1,2,3," + i);
resources.add(webannotation);
}
list.setResources(resources);
return list;
}
}