let OAIClient do all the nasty work for listing records, remove netty-http client, use JDK HTTP client

This commit is contained in:
Jörg Prante 2021-02-03 11:57:25 +01:00
parent b9df4d8199
commit e66af3ab74
23 changed files with 303 additions and 415 deletions

View file

@ -25,11 +25,6 @@ ext {
subprojects { subprojects {
apply plugin: 'java-library' apply plugin: 'java-library'
dependencies {
testImplementation "org.xbib:bibliographic-character-sets:${project.property('xbib-bibliographic-character-sets.version')}"
}
apply from: rootProject.file('gradle/ide/idea.gradle') apply from: rootProject.file('gradle/ide/idea.gradle')
apply from: rootProject.file('gradle/compile/java.gradle') apply from: rootProject.file('gradle/compile/java.gradle')
apply from: rootProject.file('gradle/test/junit5.gradle') apply from: rootProject.file('gradle/test/junit5.gradle')

View file

@ -1,10 +1,8 @@
group = org.xbib group = org.xbib
name = oai name = oai
version = 2.4.1 version = 2.5.0
gradle.wrapper.version = 6.6.1 gradle.wrapper.version = 6.6.1
xbib-content.version = 2.6.2 xbib-content.version = 2.6.2
xbib-netty-http.version = 4.1.58.0
xbib-marc.version = 2.4.0 xbib-marc.version = 2.4.0
xbib-bibliographic-character-sets.version = 2.0.0 xbib-bibliographic-character-sets.version = 2.0.0
tcnative.version = 2.0.36.Final

View file

@ -1,7 +1,5 @@
dependencies { dependencies {
api project(':oai-common') api project(':oai-common')
api "org.xbib:netty-http-client:${project.property('xbib-netty-http.version')}"
implementation "io.netty:netty-tcnative-boringssl-static:${project.property('tcnative.version')}"
testImplementation "org.xbib:marc:${project.property('xbib-marc.version')}" testImplementation "org.xbib:marc:${project.property('xbib-marc.version')}"
testImplementation "org.xbib:bibliographic-character-sets:${project.property('xbib-bibliographic-character-sets.version')}" testImplementation "org.xbib:bibliographic-character-sets:${project.property('xbib-bibliographic-character-sets.version')}"
} }

View file

@ -3,11 +3,13 @@ module org.xbib.oai.client {
exports org.xbib.oai.client.getrecord; exports org.xbib.oai.client.getrecord;
exports org.xbib.oai.client.identify; exports org.xbib.oai.client.identify;
exports org.xbib.oai.client.listidentifiers; exports org.xbib.oai.client.listidentifiers;
exports org.xbib.oai.client.listmetadataformats;
exports org.xbib.oai.client.listrecords; exports org.xbib.oai.client.listrecords;
exports org.xbib.oai.client.listsets; exports org.xbib.oai.client.listsets;
requires org.xbib.oai; requires org.xbib.oai;
requires org.xbib.net.url; requires org.xbib.net.url;
requires org.xbib.netty.http.common;
requires org.xbib.content.xml; requires org.xbib.content.xml;
requires java.xml; requires java.xml;
requires java.logging;
requires java.net.http;
} }

View file

@ -1,19 +1,20 @@
package org.xbib.oai.client; package org.xbib.oai.client;
import org.xbib.net.URL;
import org.xbib.oai.OAIConstants; import org.xbib.oai.OAIConstants;
import org.xbib.oai.OAIRequest; import org.xbib.oai.OAIRequest;
import org.xbib.oai.util.ResumptionToken; import org.xbib.oai.util.ResumptionToken;
import java.time.Instant; import java.time.Instant;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.Map;
/** /**
* Client OAI request. * Client OAI request.
*/ */
public abstract class AbstractOAIRequest implements OAIRequest { public abstract class AbstractOAIRequest implements OAIRequest {
private final URL.Builder urlBuilder; private final Map<String, String> params;
private DateTimeFormatter dateTimeFormatter; private DateTimeFormatter dateTimeFormatter;
@ -29,24 +30,20 @@ public abstract class AbstractOAIRequest implements OAIRequest {
private boolean retry; private boolean retry;
protected AbstractOAIRequest(URL url) { protected AbstractOAIRequest() {
this.urlBuilder = URL.builder() this.params = new LinkedHashMap<>();
.scheme(url.getScheme())
.host(url.getHost())
.port(url.getPort())
.path(url.getPath());
}
public URL getURL() {
return urlBuilder.build();
} }
protected void addParameter(String name, String value) { protected void addParameter(String name, String value) {
if (value != null && !value.isEmpty()) { if (value != null && !value.isEmpty()) {
urlBuilder.queryParam(name, value).build(); params.put(name, value);
} }
} }
public Map<String, String> getParams() {
return params;
}
@Override @Override
public void setSet(String set) { public void setSet(String set) {
this.set = set; this.set = set;

View file

@ -1,6 +1,5 @@
package org.xbib.oai.client; package org.xbib.oai.client;
import org.xbib.netty.http.common.HttpResponse;
import org.xbib.oai.OAIResponse; import org.xbib.oai.OAIResponse;
import org.xbib.oai.exceptions.OAIException; import org.xbib.oai.exceptions.OAIException;
@ -11,5 +10,5 @@ import java.io.Writer;
*/ */
public abstract class AbstractOAIResponse implements OAIResponse { public abstract class AbstractOAIResponse implements OAIResponse {
public abstract void receivedResponse(HttpResponse message, Writer writer) throws OAIException; public abstract void receivedResponse(String message, int status, String contentType, String retryAfter, Writer writer) throws OAIException;
} }

View file

@ -3,25 +3,47 @@ package org.xbib.oai.client;
import org.xbib.net.URL; import org.xbib.net.URL;
import org.xbib.oai.client.getrecord.GetRecordRequest; import org.xbib.oai.client.getrecord.GetRecordRequest;
import org.xbib.oai.client.identify.IdentifyRequest; import org.xbib.oai.client.identify.IdentifyRequest;
import org.xbib.oai.client.identify.IdentifyResponse;
import org.xbib.oai.client.listidentifiers.ListIdentifiersRequest; import org.xbib.oai.client.listidentifiers.ListIdentifiersRequest;
import org.xbib.oai.client.listmetadataformats.ListMetadataFormatsRequest; import org.xbib.oai.client.listmetadataformats.ListMetadataFormatsRequest;
import org.xbib.oai.client.listrecords.ListRecordsRequest; import org.xbib.oai.client.listrecords.ListRecordsRequest;
import org.xbib.oai.client.listrecords.ListRecordsResponse;
import org.xbib.oai.client.listsets.ListSetsRequest; import org.xbib.oai.client.listsets.ListSetsRequest;
import org.xbib.oai.util.ResumptionToken; import org.xbib.oai.util.ResumptionToken;
import org.xbib.oai.xml.SimpleMetadataHandler;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
/** /**
* OAI client. * OAI client.
*/ */
public class OAIClient implements AutoCloseable { public class OAIClient {
private final URL url; private static final Logger logger = Logger.getLogger(OAIClient.class.getName());
public OAIClient(URL url) { private final String baseURL;
this.url = url;
}
public URL getURL() { private final HttpClient httpClient;
return url;
public OAIClient(String baseURL) {
this.baseURL = baseURL;
this.httpClient = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.ALWAYS)
.build();
} }
/** /**
@ -29,10 +51,25 @@ public class OAIClient implements AutoCloseable {
* Some of the information returned is required as part of the OAI-PMH. * Some of the information returned is required as part of the OAI-PMH.
* Repositories may also employ the Identify verb to return additional * Repositories may also employ the Identify verb to return additional
* descriptive information. * descriptive information.
* @return identify request * @return identify response
*/ */
public IdentifyRequest newIdentifyRequest() { public IdentifyResponse identify() throws IOException, InterruptedException {
return new IdentifyRequest(url); IdentifyRequest identifyRequest = new IdentifyRequest();
IdentifyResponse identifyResponse = new IdentifyResponse();
URL.Builder url = URL.from(baseURL).mutator();
identifyRequest.getParams().forEach(url::queryParam);
HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(URI.create(url.build().toExternalForm()))
.header("accept", "utf-8")
.GET()
.build();
HttpResponse<String> httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
int status = httpResponse.statusCode();
String contentType = httpResponse.headers().firstValue("content-type").orElse(null);
String retryAfter = httpResponse.headers().firstValue("retry-after").orElse(null);
StringWriter sw = new StringWriter();
identifyResponse.receivedResponse(httpResponse.body(), status, contentType, retryAfter, sw);
return identifyResponse;
} }
/** /**
@ -42,7 +79,7 @@ public class OAIClient implements AutoCloseable {
* @return list metadata formats request * @return list metadata formats request
*/ */
public ListMetadataFormatsRequest newListMetadataFormatsRequest() { public ListMetadataFormatsRequest newListMetadataFormatsRequest() {
return new ListMetadataFormatsRequest(url); return new ListMetadataFormatsRequest();
} }
/** /**
@ -51,7 +88,7 @@ public class OAIClient implements AutoCloseable {
* @return list sets request * @return list sets request
*/ */
public ListSetsRequest newListSetsRequest() { public ListSetsRequest newListSetsRequest() {
return new ListSetsRequest(url); return new ListSetsRequest();
} }
/** /**
@ -65,7 +102,7 @@ public class OAIClient implements AutoCloseable {
* *
*/ */
public ListIdentifiersRequest newListIdentifiersRequest() { public ListIdentifiersRequest newListIdentifiersRequest() {
return new ListIdentifiersRequest(url); return new ListIdentifiersRequest();
} }
/** /**
@ -80,7 +117,7 @@ public class OAIClient implements AutoCloseable {
* @return get record request * @return get record request
*/ */
public GetRecordRequest newGetRecordRequest() { public GetRecordRequest newGetRecordRequest() {
return new GetRecordRequest(url); return new GetRecordRequest();
} }
/** /**
@ -91,10 +128,109 @@ public class OAIClient implements AutoCloseable {
* attribute of "deleted" if a record matching the arguments * attribute of "deleted" if a record matching the arguments
* specified in the request has been deleted. No metadata * specified in the request has been deleted. No metadata
* will be present for records with deleted status. * will be present for records with deleted status.
* @return list records request
*/ */
public ListRecordsRequest newListRecordsRequest() { public void listRecords(String metadataPrefix,
return new ListRecordsRequest(url); String set,
DateTimeFormatter dateTimeFormatter,
Instant from,
Instant until,
Writer writer,
SimpleMetadataHandler handler) {
ListRecordsRequest listRecordsRequest = new ListRecordsRequest();
if (metadataPrefix != null) {
listRecordsRequest.setMetadataPrefix(metadataPrefix);
}
if (set != null) {
listRecordsRequest.setSet(set);
}
if (dateTimeFormatter != null) {
listRecordsRequest.setDateTimeFormatter(dateTimeFormatter);
}
if (from != null) {
listRecordsRequest.setFrom(from);
}
if (until != null) {
listRecordsRequest.setUntil(until);
}
while (listRecordsRequest != null) {
try {
if (handler != null) {
listRecordsRequest.addHandler(handler);
}
ListRecordsResponse listRecordsResponse = new ListRecordsResponse(listRecordsRequest);
URL.Builder url = URL.from(baseURL).mutator();
listRecordsRequest.getParams().forEach(url::queryParam);
HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(URI.create(url.build().toExternalForm()))
.header("accept", "utf-8")
.GET()
.build();
logger.log(Level.INFO,"sending " + httpRequest);
HttpResponse<String> httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
int status = httpResponse.statusCode();
String contentType = httpResponse.headers().firstValue("content-type").orElse(null);
String retryAfter = httpResponse.headers().firstValue("retry-after").orElse(null);
listRecordsResponse.receivedResponse(httpResponse.body(), status, contentType, retryAfter, writer);
logger.log(Level.FINE, "response headers = " + httpResponse.headers() +
" resumption-token = " + listRecordsResponse.getResumptionToken());
listRecordsRequest = resume(listRecordsRequest, listRecordsResponse.getResumptionToken());
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
listRecordsRequest = null;
}
}
}
public void listRecords(String metadataPrefix,
String set,
DateTimeFormatter dateTimeFormatter,
Instant from,
Instant until,
Consumer<InputStream> consumer) {
ListRecordsRequest listRecordsRequest = new ListRecordsRequest();
if (metadataPrefix != null) {
listRecordsRequest.setMetadataPrefix(metadataPrefix);
}
if (set != null) {
listRecordsRequest.setSet(set);
}
if (dateTimeFormatter != null) {
listRecordsRequest.setDateTimeFormatter(dateTimeFormatter);
}
if (from != null) {
listRecordsRequest.setFrom(from);
}
if (until != null) {
listRecordsRequest.setUntil(until);
}
while (listRecordsRequest != null) {
try {
StringWriter sw = new StringWriter();
ListRecordsResponse listRecordsResponse = new ListRecordsResponse(listRecordsRequest);
URL.Builder url = URL.from(baseURL).mutator();
listRecordsRequest.getParams().forEach(url::queryParam);
HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(URI.create(url.build().toExternalForm()))
.header("accept", "utf-8")
.GET()
.build();
logger.log(Level.INFO,"sending " + httpRequest);
HttpResponse<byte[]> httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofByteArray());
int status = httpResponse.statusCode();
String contentType = httpResponse.headers().firstValue("content-type").orElse(null);
String retryAfter = httpResponse.headers().firstValue("retry-after").orElse(null);
listRecordsResponse.receivedResponse(new String(httpResponse.body(), StandardCharsets.UTF_8), status, contentType, retryAfter, sw);
if (consumer != null) {
consumer.accept(new ByteArrayInputStream(httpResponse.body()));
}
logger.log(Level.FINE, "response headers = " + httpResponse.headers() +
" resumption-token = " + listRecordsResponse.getResumptionToken());
listRecordsRequest = resume(listRecordsRequest, listRecordsResponse.getResumptionToken());
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
listRecordsRequest = null;
}
}
} }
public IdentifyRequest resume(IdentifyRequest request, ResumptionToken<?> token) { public IdentifyRequest resume(IdentifyRequest request, ResumptionToken<?> token) {
@ -105,7 +241,7 @@ public class OAIClient implements AutoCloseable {
if (token == null) { if (token == null) {
return null; return null;
} }
IdentifyRequest nextRequest = newIdentifyRequest(); IdentifyRequest nextRequest = new IdentifyRequest();
nextRequest.setResumptionToken(token); nextRequest.setResumptionToken(token);
return nextRequest; return nextRequest;
} }
@ -118,7 +254,7 @@ public class OAIClient implements AutoCloseable {
if (token == null) { if (token == null) {
return null; return null;
} }
ListRecordsRequest nextRequest = newListRecordsRequest(); ListRecordsRequest nextRequest = new ListRecordsRequest();
nextRequest.setResumptionToken(token); nextRequest.setResumptionToken(token);
return nextRequest; return nextRequest;
} }
@ -175,8 +311,4 @@ public class OAIClient implements AutoCloseable {
return nextRequest; return nextRequest;
} }
@Override
public void close() {
// nothing to close
}
} }

View file

@ -1,6 +1,5 @@
package org.xbib.oai.client.getrecord; package org.xbib.oai.client.getrecord;
import org.xbib.net.URL;
import org.xbib.oai.client.AbstractOAIRequest; import org.xbib.oai.client.AbstractOAIRequest;
/** /**
@ -8,8 +7,8 @@ import org.xbib.oai.client.AbstractOAIRequest;
*/ */
public class GetRecordRequest extends AbstractOAIRequest { public class GetRecordRequest extends AbstractOAIRequest {
public GetRecordRequest(URL url) { public GetRecordRequest() {
super(url); super();
addParameter(VERB_PARAMETER, GET_RECORD); addParameter(VERB_PARAMETER, GET_RECORD);
} }
} }

View file

@ -1,6 +1,5 @@
package org.xbib.oai.client.getrecord; package org.xbib.oai.client.getrecord;
import org.xbib.netty.http.common.HttpResponse;
import org.xbib.oai.client.AbstractOAIResponse; import org.xbib.oai.client.AbstractOAIResponse;
import java.io.Writer; import java.io.Writer;
@ -11,7 +10,7 @@ import java.io.Writer;
public class GetRecordResponse extends AbstractOAIResponse { public class GetRecordResponse extends AbstractOAIResponse {
@Override @Override
public void receivedResponse(HttpResponse message, Writer writer) { public void receivedResponse(String message, int status, String contentType, String retryAfter, Writer writer) {
// not implemented yet // not implemented yet
} }
} }

View file

@ -1,6 +1,5 @@
package org.xbib.oai.client.identify; package org.xbib.oai.client.identify;
import org.xbib.net.URL;
import org.xbib.oai.client.AbstractOAIRequest; import org.xbib.oai.client.AbstractOAIRequest;
/** /**
@ -8,8 +7,8 @@ import org.xbib.oai.client.AbstractOAIRequest;
*/ */
public class IdentifyRequest extends AbstractOAIRequest { public class IdentifyRequest extends AbstractOAIRequest {
public IdentifyRequest(URL url) { public IdentifyRequest() {
super(url); super();
addParameter(VERB_PARAMETER, IDENTIFY); addParameter(VERB_PARAMETER, IDENTIFY);
} }
} }

View file

@ -3,7 +3,6 @@ package org.xbib.oai.client.identify;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import org.xbib.netty.http.common.HttpResponse;
import org.xbib.oai.client.AbstractOAIResponse; import org.xbib.oai.client.AbstractOAIResponse;
import org.xbib.oai.exceptions.OAIException; import org.xbib.oai.exceptions.OAIException;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
@ -13,7 +12,6 @@ import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.io.Writer; import java.io.Writer;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -32,7 +30,7 @@ public class IdentifyResponse extends AbstractOAIResponse {
private String protocolVersion; private String protocolVersion;
private List<String> adminEmails = new ArrayList<>(); private final List<String> adminEmails = new ArrayList<>();
private Date earliestDatestamp; private Date earliestDatestamp;
@ -43,11 +41,11 @@ public class IdentifyResponse extends AbstractOAIResponse {
private String compression; private String compression;
@Override @Override
public void receivedResponse(HttpResponse message, Writer writer) { public void receivedResponse(String message, int statusCode, String contentType, String retryAfter, Writer writer) {
try { try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder(); DocumentBuilder db = dbf.newDocumentBuilder();
InputSource is = new InputSource(new StringReader(message.getBodyAsString(StandardCharsets.UTF_8))); InputSource is = new InputSource(new StringReader(message));
Document doc = db.parse(is); Document doc = db.parse(is);
setGranularity(getString("granularity", doc.getDocumentElement())); setGranularity(getString("granularity", doc.getDocumentElement()));
} catch (ParserConfigurationException | SAXException | IOException e) { } catch (ParserConfigurationException | SAXException | IOException e) {

View file

@ -1,6 +1,5 @@
package org.xbib.oai.client.listidentifiers; package org.xbib.oai.client.listidentifiers;
import org.xbib.net.URL;
import org.xbib.oai.client.AbstractOAIRequest; import org.xbib.oai.client.AbstractOAIRequest;
/** /**
@ -8,8 +7,8 @@ import org.xbib.oai.client.AbstractOAIRequest;
*/ */
public class ListIdentifiersRequest extends AbstractOAIRequest { public class ListIdentifiersRequest extends AbstractOAIRequest {
public ListIdentifiersRequest(URL url) { public ListIdentifiersRequest() {
super(url); super();
addParameter(VERB_PARAMETER, LIST_IDENTIFIERS); addParameter(VERB_PARAMETER, LIST_IDENTIFIERS);
} }
} }

View file

@ -1,6 +1,5 @@
package org.xbib.oai.client.listidentifiers; package org.xbib.oai.client.listidentifiers;
import org.xbib.netty.http.common.HttpResponse;
import org.xbib.oai.client.AbstractOAIResponse; import org.xbib.oai.client.AbstractOAIResponse;
import org.xbib.oai.exceptions.OAIException; import org.xbib.oai.exceptions.OAIException;
@ -12,7 +11,7 @@ import java.io.Writer;
public class ListIdentifiersResponse extends AbstractOAIResponse { public class ListIdentifiersResponse extends AbstractOAIResponse {
@Override @Override
public void receivedResponse(HttpResponse message, Writer writer) throws OAIException { public void receivedResponse(String message, int statusCode, String contentTyep, String retryAfter, Writer writer) throws OAIException {
// not implemented yet // not implemented yet
} }
} }

View file

@ -1,6 +1,5 @@
package org.xbib.oai.client.listmetadataformats; package org.xbib.oai.client.listmetadataformats;
import org.xbib.net.URL;
import org.xbib.oai.client.AbstractOAIRequest; import org.xbib.oai.client.AbstractOAIRequest;
/** /**
@ -8,8 +7,8 @@ import org.xbib.oai.client.AbstractOAIRequest;
*/ */
public class ListMetadataFormatsRequest extends AbstractOAIRequest { public class ListMetadataFormatsRequest extends AbstractOAIRequest {
public ListMetadataFormatsRequest(URL url) { public ListMetadataFormatsRequest() {
super(url); super();
addParameter(VERB_PARAMETER, LIST_METADATA_FORMATS); addParameter(VERB_PARAMETER, LIST_METADATA_FORMATS);
} }
} }

View file

@ -1,6 +1,5 @@
package org.xbib.oai.client.listmetadataformats; package org.xbib.oai.client.listmetadataformats;
import org.xbib.netty.http.common.HttpResponse;
import org.xbib.oai.client.AbstractOAIResponse; import org.xbib.oai.client.AbstractOAIResponse;
import org.xbib.oai.exceptions.OAIException; import org.xbib.oai.exceptions.OAIException;
@ -12,7 +11,7 @@ import java.io.Writer;
public class ListMetadataFormatsResponse extends AbstractOAIResponse { public class ListMetadataFormatsResponse extends AbstractOAIResponse {
@Override @Override
public void receivedResponse(HttpResponse message, Writer writer) throws OAIException { public void receivedResponse(String message, int statusCode, String contentType, String retryAfter, Writer writer) throws OAIException {
// not implemented yet // not implemented yet
} }
} }

View file

@ -1,6 +1,5 @@
package org.xbib.oai.client.listrecords; package org.xbib.oai.client.listrecords;
import org.xbib.net.URL;
import org.xbib.oai.OAIConstants; import org.xbib.oai.OAIConstants;
import org.xbib.oai.client.AbstractOAIRequest; import org.xbib.oai.client.AbstractOAIRequest;
import org.xbib.oai.xml.MetadataHandler; import org.xbib.oai.xml.MetadataHandler;
@ -15,8 +14,8 @@ public class ListRecordsRequest extends AbstractOAIRequest {
private List<MetadataHandler> handlers = new LinkedList<>(); private List<MetadataHandler> handlers = new LinkedList<>();
public ListRecordsRequest(URL url) { public ListRecordsRequest() {
super(url); super();
addParameter(OAIConstants.VERB_PARAMETER, LIST_RECORDS); addParameter(OAIConstants.VERB_PARAMETER, LIST_RECORDS);
} }
public ListRecordsRequest addHandler(MetadataHandler handler) { public ListRecordsRequest addHandler(MetadataHandler handler) {

View file

@ -2,7 +2,6 @@ package org.xbib.oai.client.listrecords;
import org.xbib.content.xml.transform.TransformerURIResolver; import org.xbib.content.xml.transform.TransformerURIResolver;
import org.xbib.content.xml.util.XMLUtil; import org.xbib.content.xml.util.XMLUtil;
import org.xbib.netty.http.common.HttpResponse;
import org.xbib.oai.client.AbstractOAIResponse; import org.xbib.oai.client.AbstractOAIResponse;
import org.xbib.oai.exceptions.BadVerbException; import org.xbib.oai.exceptions.BadVerbException;
import org.xbib.oai.exceptions.BadArgumentException; import org.xbib.oai.exceptions.BadArgumentException;
@ -14,7 +13,6 @@ import org.xml.sax.InputSource;
import java.io.StringReader; import java.io.StringReader;
import java.io.Writer; import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.time.Instant; import java.time.Instant;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
@ -71,23 +69,15 @@ public class ListRecordsResponse extends AbstractOAIResponse {
} }
@Override @Override
public void receivedResponse(HttpResponse message, Writer writer) throws OAIException { public void receivedResponse(String message, int status, String contentType, String retryAfter, Writer writer) throws OAIException {
String content = message.getBodyAsString(StandardCharsets.UTF_8);
int status = message.getStatus().getCode();
if (status == 503) { if (status == 503) {
long secs = retryAfterMillis / 1000; long secs = retryAfterMillis / 1000;
if (message.getHeaders() != null) { if (retryAfter != null) {
for (String retryAfterHeader : RETRY_AFTER_HEADERS) { secs = Long.parseLong(retryAfter);
String retryAfter = message.getHeaders().getHeader(retryAfterHeader); if (!isDigits(retryAfter)) {
if (retryAfter == null) { // parse RFC date, e.g. Fri, 31 Dec 1999 23:59:59 GMT
continue; Instant instant = Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(retryAfter));
} secs = ChronoUnit.SECONDS.between(instant, Instant.now());
secs = Long.parseLong(retryAfter);
if (!isDigits(retryAfter)) {
// parse RFC date, e.g. Fri, 31 Dec 1999 23:59:59 GMT
Instant instant = Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(retryAfter));
secs = ChronoUnit.SECONDS.between(instant, Instant.now());
}
} }
} }
request.setRetry(true); request.setRetry(true);
@ -108,37 +98,39 @@ public class ListRecordsResponse extends AbstractOAIResponse {
} }
} }
if (status != 200) { if (status != 200) {
throw new OAIException("status = " + status + " response = " + content); throw new OAIException("status = " + status + " response = " + message);
} }
// activate XSLT only if OAI XML content type is returned // activate XSLT only if OAI XML content type is returned
String contentType = message.getHeaders().getHeader("content-type");
if (contentType != null && !contentType.startsWith("text/xml")) { if (contentType != null && !contentType.startsWith("text/xml")) {
throw new OAIException("no XML content type in response: " + contentType); throw new OAIException("no XML content type in response: " + contentType);
} }
// the filterreader allows access to the resumption token
this.filterreader = new ListRecordsFilterReader(request, this); this.filterreader = new ListRecordsFilterReader(request, this);
try { if (message != null) {
TransformerFactory transformerFactory = TransformerFactory.newInstance(); try {
transformerFactory.setURIResolver(new TransformerURIResolver("xsl")); TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer(); transformerFactory.setURIResolver(new TransformerURIResolver("xsl"));
Source source = new SAXSource(filterreader, new InputSource(new StringReader(XMLUtil.sanitize(content)))); Transformer transformer = transformerFactory.newTransformer();
StreamResult streamResult = new StreamResult(writer); Source source = new SAXSource(filterreader, new InputSource(new StringReader(XMLUtil.sanitize(message))));
transformer.transform(source, streamResult); StreamResult streamResult = new StreamResult(writer);
if ("noRecordsMatch".equals(error)) { transformer.transform(source, streamResult);
throw new NoRecordsMatchException("metadataPrefix=" + request.getMetadataPrefix() if ("noRecordsMatch".equals(error)) {
+ ",set=" + request.getSet() throw new NoRecordsMatchException("metadataPrefix=" + request.getMetadataPrefix()
+ ",from=" + request.getFrom() + ",set=" + request.getSet()
+ ",until=" + request.getUntil()); + ",from=" + request.getFrom()
} else if ("badResumptionToken".equals(error)) { + ",until=" + request.getUntil());
throw new BadResumptionTokenException(request.getResumptionToken()); } else if ("badResumptionToken".equals(error)) {
} else if ("badArgument".equals(error)) { throw new BadResumptionTokenException(request.getResumptionToken());
throw new BadArgumentException(); } else if ("badArgument".equals(error)) {
} else if ("badVerb".equals(error)) { throw new BadArgumentException();
throw new BadVerbException(error); } else if ("badVerb".equals(error)) {
} else if (error != null) { throw new BadVerbException(error);
throw new OAIException(error); } else if (error != null) {
throw new OAIException(error);
}
} catch (TransformerException t) {
throw new OAIException(t);
} }
} catch (TransformerException t) {
throw new OAIException(t);
} }
} }

View file

@ -1,6 +1,5 @@
package org.xbib.oai.client.listsets; package org.xbib.oai.client.listsets;
import org.xbib.net.URL;
import org.xbib.oai.client.AbstractOAIRequest; import org.xbib.oai.client.AbstractOAIRequest;
/** /**
@ -8,8 +7,8 @@ import org.xbib.oai.client.AbstractOAIRequest;
*/ */
public class ListSetsRequest extends AbstractOAIRequest { public class ListSetsRequest extends AbstractOAIRequest {
public ListSetsRequest(URL url) { public ListSetsRequest() {
super(url); super();
addParameter(VERB_PARAMETER, LIST_SETS); addParameter(VERB_PARAMETER, LIST_SETS);
} }

View file

@ -1,6 +1,5 @@
package org.xbib.oai.client.listsets; package org.xbib.oai.client.listsets;
import org.xbib.netty.http.common.HttpResponse;
import org.xbib.oai.client.AbstractOAIResponse; import org.xbib.oai.client.AbstractOAIResponse;
import org.xbib.oai.exceptions.OAIException; import org.xbib.oai.exceptions.OAIException;
@ -12,7 +11,7 @@ import java.io.Writer;
public class ListSetsResponse extends AbstractOAIResponse { public class ListSetsResponse extends AbstractOAIResponse {
@Override @Override
public void receivedResponse(HttpResponse message, Writer writer) throws OAIException { public void receivedResponse(String message, int statusCode, String contentType, String retryAfter, Writer writer) throws OAIException {
// not implemented yet // not implemented yet
} }
} }

View file

@ -1,22 +1,13 @@
package org.xbib.oai.client; package org.xbib.oai.client;
import io.netty.handler.codec.http.HttpHeaderNames;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.xbib.net.URL;
import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.api.Request;
import org.xbib.oai.client.identify.IdentifyRequest;
import org.xbib.oai.client.identify.IdentifyResponse; import org.xbib.oai.client.identify.IdentifyResponse;
import org.xbib.oai.client.listrecords.ListRecordsRequest;
import org.xbib.oai.client.listrecords.ListRecordsResponse;
import org.xbib.oai.xml.SimpleMetadataHandler; import org.xbib.oai.xml.SimpleMetadataHandler;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.time.Instant; import java.time.Instant;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
@ -24,77 +15,30 @@ import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/**
*
*/
class ArxivClientTest { class ArxivClientTest {
private static final Logger logger = Logger.getLogger(ArxivClientTest.class.getName()); private static final Logger logger = Logger.getLogger(ArxivClientTest.class.getName());
@Test @Test
void testListRecordsArxiv() { void testListRecordsArxivWithJdkClient() throws Exception {
final URL url = URL.create("http://export.arxiv.org/oai2/"); OAIClient oaiClient = new OAIClient("http://export.arxiv.org/oai2/");
try (Client httpClient = Client.builder() IdentifyResponse identifyResponse = oaiClient.identify();
.setConnectTimeoutMillis(60 * 1000) String granularity = identifyResponse.getGranularity();
.setReadTimeoutMillis(60 * 1000) logger.log(Level.INFO, "granularity = " + granularity);
.build(); DateTimeFormatter dateTimeFormatter = "YYYY-MM-DD".equals(granularity) ?
OAIClient client = new OAIClient(url)) {
IdentifyRequest identifyRequest = client.newIdentifyRequest();
IdentifyResponse identifyResponse = new IdentifyResponse();
Request request = Request.get()
.url(identifyRequest.getURL())
.addHeader(HttpHeaderNames.ACCEPT.toString(), "utf-8")
.setResponseListener(resp -> {
logger.log(Level.INFO,
" body = " + resp.getBodyAsString(StandardCharsets.UTF_8));
StringWriter sw = new StringWriter();
identifyResponse.receivedResponse(resp, sw);
})
.build();
httpClient.execute(request).get();
String granularity = identifyResponse.getGranularity();
logger.log(Level.INFO, "granularity = " + granularity);
DateTimeFormatter dateTimeFormatter = "YYYY-MM-DD".equals(granularity) ?
DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.of("GMT")) : null; DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.of("GMT")) : null;
// ArXiv wants us to wait 20 secs between *every* HTTP request, so we must wait here // ArXiv wants us to wait 20 secs between *every* HTTP request, so we must wait here
logger.log(Level.INFO,"waiting 20 seconds"); logger.log(Level.INFO,"waiting 20 seconds");
Thread.sleep(20 * 1000L); Thread.sleep(20 * 1000L);
ListRecordsRequest listRecordsRequest = client.newListRecordsRequest(); Handler handler = new Handler();
listRecordsRequest.setDateTimeFormatter(dateTimeFormatter); File file = File.createTempFile("arxiv.", ".xml");
listRecordsRequest.setFrom(Instant.parse("2016-11-01T00:00:00Z")); file.deleteOnExit();
listRecordsRequest.setUntil(Instant.parse("2016-11-02T00:00:00Z")); FileWriter fileWriter = new FileWriter(file);
listRecordsRequest.setMetadataPrefix("arXiv"); oaiClient.listRecords("arXiv", null,
Handler handler = new Handler(); dateTimeFormatter, Instant.parse("2016-11-01T00:00:00Z"), Instant.parse("2016-11-02T00:00:00Z"), fileWriter, handler);
File file = File.createTempFile("arxiv.", ".xml"); fileWriter.close();
file.deleteOnExit(); logger.log(Level.INFO, "count = " + handler.count());
FileWriter fileWriter = new FileWriter(file); assertTrue(handler.count() > 0L);
while (listRecordsRequest != null) {
try {
listRecordsRequest.addHandler(handler);
ListRecordsResponse listRecordsResponse = new ListRecordsResponse(listRecordsRequest);
logger.log(Level.INFO,"sending " + listRecordsRequest.getURL());
request = Request.get()
.url(listRecordsRequest.getURL())
.addHeader(HttpHeaderNames.ACCEPT.toString(), "utf-8")
.setResponseListener(resp -> {
listRecordsResponse.receivedResponse(resp, fileWriter);
logger.log(Level.FINE, "response headers = " + resp.getHeaders() +
" resumption-token = " + listRecordsResponse.getResumptionToken());
})
.build();
httpClient.execute(request).get();
listRecordsRequest = client.resume(listRecordsRequest, listRecordsResponse.getResumptionToken());
} catch (IOException e) {
logger.log(Level.SEVERE, e.getMessage(), e);
listRecordsRequest = null;
}
}
fileWriter.close();
logger.log(Level.INFO, "count = " + handler.count());
assertTrue(handler.count() > 0L);
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
} }
static class Handler extends SimpleMetadataHandler { static class Handler extends SimpleMetadataHandler {

View file

@ -1,23 +1,13 @@
package org.xbib.oai.client; package org.xbib.oai.client;
import io.netty.handler.codec.http.HttpHeaderNames;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.xbib.marc.Marc; import org.xbib.marc.Marc;
import org.xbib.marc.json.MarcJsonWriter; import org.xbib.marc.json.MarcJsonWriter;
import org.xbib.marc.xml.MarcContentHandler; import org.xbib.marc.xml.MarcContentHandler;
import org.xbib.net.URL;
import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.api.Request;
import org.xbib.oai.client.identify.IdentifyRequest;
import org.xbib.oai.client.identify.IdentifyResponse; import org.xbib.oai.client.identify.IdentifyResponse;
import org.xbib.oai.client.listrecords.ListRecordsRequest;
import org.xbib.oai.client.listrecords.ListRecordsResponse;
import org.xbib.oai.exceptions.OAIException; import org.xbib.oai.exceptions.OAIException;
import java.io.IOException; import java.io.IOException;
import java.io.StringWriter;
import java.net.ConnectException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
@ -25,96 +15,44 @@ import java.util.EnumSet;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/**
*
*/
class BundeskunsthalleTest { class BundeskunsthalleTest {
private static final Logger logger = Logger.getLogger(BundeskunsthalleTest.class.getName()); private static final Logger logger = Logger.getLogger(BundeskunsthalleTest.class.getName());
@Test @Test
@Disabled("takes long time") // @Disabled("takes long time")
void testListRecords() { void testListRecords() throws Exception {
URL url = URL.create("https://www.bundeskunsthalle.de/cgi-bin/bib/oai-pmh"); OAIClient oaiClient = new OAIClient("https://www.bundeskunsthalle.de/cgi-bin/bib/oai-pmh");
try (Client httpClient = Client.builder() IdentifyResponse identifyResponse = oaiClient.identify();
.setConnectTimeoutMillis(60 * 1000) String granularity = identifyResponse.getGranularity();
.setReadTimeoutMillis(60 * 1000) logger.log(Level.INFO, "granularity = " + granularity);
.build(); DateTimeFormatter dateTimeFormatter = "YYYY-MM-DD".equals(granularity) ?
OAIClient oaiClient = new OAIClient(url)) { DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.of("UTC")) : null;
IdentifyRequest identifyRequest = oaiClient.newIdentifyRequest(); try (MarcJsonWriter writer = new MarcJsonWriter("build/bk-bulk%d.jsonl", 1000,
IdentifyResponse identifyResponse = new IdentifyResponse(); EnumSet.of(MarcJsonWriter.Style.ELASTICSEARCH_BULK), 65536, false)
Request request = Request.get() .setIndex("bk", "type")) {
.url(identifyRequest.getURL()) writer.startDocument();
.addHeader(HttpHeaderNames.ACCEPT.toString(), "utf-8") writer.beginCollection();
.setFollowRedirect(true) oaiClient.listRecords("marcxml", null,
.setResponseListener(resp -> { dateTimeFormatter, null, null, inputStream -> {
logger.log(Level.INFO,
"status = " + resp.getStatus() +
" body = " + resp.getBodyAsString(StandardCharsets.UTF_8));
StringWriter sw = new StringWriter();
identifyResponse.receivedResponse(resp, sw);
})
.build();
httpClient.execute(request).get();
String granularity = identifyResponse.getGranularity();
logger.log(Level.INFO, "granularity = " + granularity);
DateTimeFormatter dateTimeFormatter = "YYYY-MM-DD".equals(granularity) ?
DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.of("UTC")) : null;
ListRecordsRequest listRecordsRequest = oaiClient.newListRecordsRequest();
listRecordsRequest.setDateTimeFormatter(dateTimeFormatter);
listRecordsRequest.setMetadataPrefix("marcxml");
try (MarcJsonWriter writer = new MarcJsonWriter("build/bk-bulk%d.jsonl", 1000,
EnumSet.of(MarcJsonWriter.Style.ELASTICSEARCH_BULK), 65536, false)
.setIndex("testindex", "testtype")) {
writer.startDocument();
writer.beginCollection();
while (listRecordsRequest != null) {
try { try {
ListRecordsResponse listRecordsResponse = new ListRecordsResponse(listRecordsRequest); Marc.builder()
logger.log(Level.INFO, "sending " + listRecordsRequest.getURL()); .setInputStream(inputStream)
request = Request.get() .setCharset(StandardCharsets.UTF_8)
.url(listRecordsRequest.getURL()) .setContentHandler(new MarcContentHandler()
.addHeader(HttpHeaderNames.ACCEPT.toString(), "utf-8") .setFormat("MarcXML")
.setFollowRedirect(true) .setType("Bibliographic")
.setTimeoutInMillis(60 * 1000) .addNamespace("http://www.loc.gov/MARC21/slim")
.setResponseListener(resp -> { .setMarcListener(writer))
logger.log(Level.FINE, .build()
"status = " + resp.getStatus() + .xmlReader().parse();
" headers = " + resp.getHeaders() +
" resumptiontoken = " + listRecordsResponse.getResumptionToken());
StringWriter sw = new StringWriter();
listRecordsResponse.receivedResponse(resp, sw);
try {
Marc.builder()
.setInputStream(resp.getBodyAsStream())
.setCharset(StandardCharsets.UTF_8)
.setContentHandler(new MarcContentHandler()
.setFormat("MarcXML")
.setType("Bibliographic")
.addNamespace("http://www.loc.gov/MARC21/slim")
.setMarcListener(writer))
.build()
.xmlReader().parse();
} catch (IOException e) {
throw new OAIException("MARC parser exception: " + e.getMessage(), e);
}
})
.build();
httpClient.execute(request).get();
listRecordsRequest = oaiClient.resume(listRecordsRequest, listRecordsResponse.getResumptionToken());
} catch (ConnectException e) {
logger.log(Level.WARNING, e.getMessage(), e);
} catch (IOException e) { } catch (IOException e) {
logger.log(Level.SEVERE, e.getMessage(), e); throw new OAIException("MARC parser exception: " + e.getMessage(), e);
listRecordsRequest = null;
} }
} });
writer.endCollection(); writer.endCollection();
writer.endDocument(); writer.endDocument();
}
logger.log(Level.INFO, "completed");
} catch (Exception e) {
logger.log(Level.WARNING, e.getMessage(), e);
} }
logger.log(Level.INFO, "completed");
} }
} }

View file

@ -1,88 +1,40 @@
package org.xbib.oai.client; package org.xbib.oai.client;
import io.netty.handler.codec.http.HttpHeaderNames;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.xbib.net.URL;
import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.api.Request;
import org.xbib.oai.client.identify.IdentifyRequest;
import org.xbib.oai.client.identify.IdentifyResponse; import org.xbib.oai.client.identify.IdentifyResponse;
import org.xbib.oai.client.listrecords.ListRecordsRequest;
import org.xbib.oai.client.listrecords.ListRecordsResponse;
import org.xbib.oai.xml.SimpleMetadataHandler; import org.xbib.oai.xml.SimpleMetadataHandler;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.net.ConnectException;
import java.nio.charset.StandardCharsets;
import java.time.Instant; import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/**
*
*/
class DNBClientTest { class DNBClientTest {
private static final Logger logger = Logger.getLogger(DNBClientTest.class.getName()); private static final Logger logger = Logger.getLogger(DNBClientTest.class.getName());
@Test @Test
void testBibdat() { void testBibdat() throws Exception {
URL url = URL.create("http://services.dnb.de/oai/repository"); OAIClient oaiClient = new OAIClient("http://services.dnb.de/oai/repository");
try (Client httpClient = Client.builder() IdentifyResponse identifyResponse = oaiClient.identify();
.setConnectTimeoutMillis(60 * 1000) String granularity = identifyResponse.getGranularity();
.setReadTimeoutMillis(60 * 1000) logger.log(Level.INFO, "granularity = " + granularity);
.build(); DateTimeFormatter dateTimeFormatter = "YYYY-MM-DD".equals(granularity) ?
OAIClient oaiClient = new OAIClient(url)) { DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.of("UTC")) : null;
IdentifyRequest identifyRequest = oaiClient.newIdentifyRequest(); Handler handler = new Handler();
IdentifyResponse identifyResponse = new IdentifyResponse(); File file = new File("build/dnb-bib-pica.xml");
Request request = Request.get() try (FileWriter fileWriter = new FileWriter(file)) {
.url(identifyRequest.getURL()) oaiClient.listRecords("PicaPlus-xml", "bib",
.setResponseListener(resp -> { dateTimeFormatter, Instant.parse("2016-01-01T00:00:00Z"), Instant.parse("2016-01-10T00:00:00Z"),
logger.log(Level.INFO, resp.getBodyAsString(StandardCharsets.UTF_8)); fileWriter, handler);
StringWriter sw = new StringWriter();
identifyResponse.receivedResponse(resp, sw);
})
.build();
httpClient.execute(request).get();
String granularity = identifyResponse.getGranularity();
logger.log(Level.INFO, "granularity = " + granularity);
ListRecordsRequest listRecordsRequest = oaiClient.newListRecordsRequest();
listRecordsRequest.setFrom(Instant.parse("2016-01-01T00:00:00Z"));
listRecordsRequest.setUntil(Instant.parse("2016-01-10T00:00:00Z"));
listRecordsRequest.setSet("bib");
listRecordsRequest.setMetadataPrefix("PicaPlus-xml");
Handler handler = new Handler();
File file = new File("build/dnb-bib-pica.xml");
FileWriter fileWriter = new FileWriter(file);
while (listRecordsRequest != null) {
try {
ListRecordsResponse listRecordsResponse = new ListRecordsResponse(listRecordsRequest);
listRecordsRequest.addHandler(handler);
request = Request.get()
.url(listRecordsRequest.getURL())
.addHeader(HttpHeaderNames.ACCEPT.toString(), "utf-8")
.setResponseListener(resp -> listRecordsResponse.receivedResponse(resp, fileWriter))
.build();
httpClient.execute(request).get();
listRecordsRequest = oaiClient.resume(listRecordsRequest, listRecordsResponse.getResumptionToken());
} catch (ConnectException e) {
logger.log(Level.WARNING, e.getMessage(), e);
} catch (IOException e) {
logger.log(Level.SEVERE, e.getMessage(), e);
listRecordsRequest = null;
}
}
fileWriter.close();
logger.log(Level.INFO, "count=" + handler.count());
assertTrue(handler.count() > 0);
} catch (Exception e) {
logger.log(Level.SEVERE, "skipped, HTTP exception");
} }
logger.log(Level.INFO, "count=" + handler.count());
assertTrue(handler.count() > 0);
} }
static class Handler extends SimpleMetadataHandler { static class Handler extends SimpleMetadataHandler {

View file

@ -1,18 +1,9 @@
package org.xbib.oai.client; package org.xbib.oai.client;
import io.netty.handler.codec.http.HttpHeaderNames;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.xbib.net.URL;
import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.api.Request;
import org.xbib.oai.client.identify.IdentifyRequest;
import org.xbib.oai.client.identify.IdentifyResponse; import org.xbib.oai.client.identify.IdentifyResponse;
import org.xbib.oai.client.listrecords.ListRecordsRequest;
import org.xbib.oai.client.listrecords.ListRecordsResponse;
import org.xbib.oai.xml.SimpleMetadataHandler; import org.xbib.oai.xml.SimpleMetadataHandler;
import java.io.StringWriter;
import java.io.Writer; import java.io.Writer;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
@ -23,66 +14,28 @@ import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** import static org.junit.jupiter.api.Assertions.assertTrue;
*
*/
class DOAJClientTest { class DOAJClientTest {
private static final Logger logger = Logger.getLogger(DOAJClientTest.class.getName()); private static final Logger logger = Logger.getLogger(DOAJClientTest.class.getName());
@Test @Test
@Disabled("takes long time") void testListRecordsDOAJ() throws Exception {
void testListRecordsDOAJ() { OAIClient oaiClient = new OAIClient("https://doaj.org/oai");
URL url = URL.create("https://doaj.org/oai"); IdentifyResponse identifyResponse = oaiClient.identify();
try (Client httpClient = Client.builder() String granularity = identifyResponse.getGranularity();
.setConnectTimeoutMillis(60 * 1000) logger.log(Level.INFO, "granularity = " + granularity);
.setReadTimeoutMillis(60 * 1000) DateTimeFormatter dateTimeFormatter = "YYYY-MM-DD".equals(granularity) ?
.build();
OAIClient oaiClient = new OAIClient(url)) {
IdentifyRequest identifyRequest = oaiClient.newIdentifyRequest();
IdentifyResponse identifyResponse = new IdentifyResponse();
Request request = Request.get()
.url(url.resolve(identifyRequest.getURL()))
.addHeader(HttpHeaderNames.ACCEPT.toString(), "utf-8")
.setResponseListener(resp -> {
StringWriter sw = new StringWriter();
identifyResponse.receivedResponse(resp, sw);
})
.build();
httpClient.execute(request).get();
String granularity = identifyResponse.getGranularity();
logger.log(Level.INFO, "granularity = " + granularity);
DateTimeFormatter dateTimeFormatter = "YYYY-MM-DD".equals(granularity) ?
DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.of("GMT")) : null; DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.of("GMT")) : null;
ListRecordsRequest listRecordsRequest = oaiClient.newListRecordsRequest(); Handler handler = new Handler();
listRecordsRequest.setDateTimeFormatter(dateTimeFormatter); try (Writer writer = Files.newBufferedWriter(Paths.get("build/doaj.xml"))) {
listRecordsRequest.setFrom(Instant.parse("2008-01-01T00:00:00Z")); oaiClient.listRecords("oai_dc", null,
listRecordsRequest.setUntil(Instant.parse("2018-01-01T00:00:00Z")); dateTimeFormatter,Instant.parse("2021-01-01T00:00:00Z"), Instant.parse("2021-02-01T00:00:00Z"),
listRecordsRequest.setMetadataPrefix("oai_dc"); writer, handler);
Handler handler = new Handler();
try (Writer writer = Files.newBufferedWriter(Paths.get("build/doaj.xml"))) {
while (listRecordsRequest != null) {
ListRecordsResponse listRecordsResponse = new ListRecordsResponse(listRecordsRequest);
listRecordsRequest.addHandler(handler);
logger.log(Level.INFO, "sending " + listRecordsRequest.getURL());
request = Request.get()
.url(url.resolve(listRecordsRequest.getURL()))
.addHeader(HttpHeaderNames.ACCEPT.toString(), "utf-8")
.setResponseListener(resp -> {
listRecordsResponse.receivedResponse(resp, writer);
logger.log(Level.FINE, "response headers = " + resp.getHeaders() +
" resumption-token = {}" + listRecordsResponse.getResumptionToken());
})
.build();
httpClient.execute(request).get();
listRecordsRequest = oaiClient.resume(listRecordsRequest, listRecordsResponse.getResumptionToken());
}
}
logger.log(Level.INFO, "count = " + handler.count());
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
} }
logger.log(Level.INFO, "count = " + handler.count());
assertTrue(handler.count() > 0);
} }
static class Handler extends SimpleMetadataHandler { static class Handler extends SimpleMetadataHandler {