RDFLoaderTest.java
/*******************************************************************************
* Copyright (c) 2021 Eclipse RDF4J contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
package org.eclipse.rdf4j.repository.util;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.eclipse.rdf4j.model.util.Statements.statement;
import static org.eclipse.rdf4j.model.util.Values.getValueFactory;
import static org.eclipse.rdf4j.model.util.Values.iri;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;
import java.net.ProtocolException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.eclipse.rdf4j.model.vocabulary.FOAF;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.eclipse.rdf4j.rio.ParserConfig;
import org.eclipse.rdf4j.rio.RDFFormat;
import org.eclipse.rdf4j.rio.RDFHandler;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockserver.client.MockServerClient;
import org.mockserver.junit.jupiter.MockServerExtension;
import org.mockserver.model.MediaType;
/**
* Unit tests for {@link RDFLoader}.
*
* @author Manuel Fiorelli
*/
@ExtendWith(MockServerExtension.class)
public class RDFLoaderTest {
@BeforeAll
static void defineMockServerBehavior(MockServerClient client) {
client.when(
request()
.withMethod("GET")
.withPath("/Socrates.ttl")
)
.respond(
response()
.withContentType(MediaType.parse(RDFFormat.TURTLE.getDefaultMIMEType()))
.withBody("<http://example.org/Socrates> a <http://xmlns.com/foaf/0.1/Person> .")
);
client.when(
request()
.withMethod("GET")
.withPath("/Socrates")
)
.respond(
response()
.withStatusCode(301)
.withHeader("Location", "/Socrates.ttl")
);
client.when(
request()
.withMethod("GET")
.withPath("/Socrates1")
)
.respond(
response()
.withStatusCode(301)
.withHeader("Location", "/Socrates2")
);
client.when(
request()
.withMethod("GET")
.withPath("/Socrates2")
)
.respond(
response()
.withStatusCode(301)
.withHeader("Location", "/Socrates.ttl")
);
}
@Test
public void testTurtleJavaResource() throws Exception {
RDFLoader rdfLoader = new RDFLoader(new ParserConfig(), getValueFactory());
RDFHandler rdfHandler = mock(RDFHandler.class);
rdfLoader.load(this.getClass().getResource("Socrates.ttl"), null, RDFFormat.TURTLE, rdfHandler);
verify(rdfHandler).startRDF();
verify(rdfHandler)
.handleStatement(statement(iri("http://example.org/Socrates"),
RDF.TYPE,
FOAF.PERSON, null));
verify(rdfHandler).endRDF();
}
@Test
public void testTurtleDocument(MockServerClient client) throws Exception {
RDFLoader rdfLoader = new RDFLoader(new ParserConfig(), getValueFactory());
RDFHandler rdfHandler = mock(RDFHandler.class);
rdfLoader.load(new URL("http://localhost:" + client.getPort() + "/Socrates.ttl"), null, null,
rdfHandler);
verify(rdfHandler).startRDF();
verify(rdfHandler)
.handleStatement(statement(iri("http://example.org/Socrates"),
RDF.TYPE,
FOAF.PERSON, null));
verify(rdfHandler).endRDF();
}
@Test
public void testMultipleRedirects(MockServerClient client) throws Exception {
RDFLoader rdfLoader = new RDFLoader(new ParserConfig(), getValueFactory());
RDFHandler rdfHandler = mock(RDFHandler.class);
rdfLoader.load(new URL("http://localhost:" + client.getPort() + "/Socrates1"), null, null,
rdfHandler);
verify(rdfHandler).startRDF();
verify(rdfHandler)
.handleStatement(statement(iri("http://example.org/Socrates"),
RDF.TYPE,
FOAF.PERSON, null));
verify(rdfHandler).endRDF();
}
@Test
public void testAbortOverMaxRedirects(MockServerClient client) throws Exception {
/* nullable */
String oldMaxRedirects = System.getProperty("http.maxRedirects");
try {
ProtocolException actualException = null;
System.setProperty("http.maxRedirects", "2"); // http.maxRedirects seems exclusive in http URL
RDFLoader rdfLoader = new RDFLoader(new ParserConfig(), getValueFactory());
RDFHandler rdfHandler = mock(RDFHandler.class);
try {
rdfLoader.load(new URL("http://localhost:" + client.getPort() + "/Socrates1"), null, null,
rdfHandler);
} catch (ProtocolException e) {
actualException = e;
}
assertThat(actualException)
.hasMessageStartingWith("Server redirected too many times");
} finally {
if (oldMaxRedirects != null) {
System.setProperty("http.maxRedirects", oldMaxRedirects);
} else {
System.getProperties().remove("http.maxRedirects");
}
}
}
@Test
public void testNonInformationResource(MockServerClient client) throws Exception {
final SSLSocketFactory toRestoreSocketFactory = disableSSLCertificatCheck();
try {
final HostnameVerifier toRestoreHostnameVerifier = disableHostnameVerifier();
try {
RDFLoader rdfLoader = new RDFLoader(new ParserConfig(), getValueFactory());
RDFHandler rdfHandler = mock(RDFHandler.class);
rdfLoader.load(new URL("http://localhost:" + client.getPort() + "/Socrates"), null, null,
rdfHandler);
verify(rdfHandler).startRDF();
verify(rdfHandler)
.handleStatement(statement(
iri("http://example.org/Socrates"),
RDF.TYPE,
FOAF.PERSON, null));
verify(rdfHandler).endRDF();
} finally {
restoreHostnameVerifier(toRestoreHostnameVerifier);
}
} finally {
restoreSocketFactory(toRestoreSocketFactory);
}
}
private static HostnameVerifier disableHostnameVerifier() {
HostnameVerifier replaced = HttpsURLConnection.getDefaultHostnameVerifier();
// set a hostname verifier that just returns true for every request
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
return replaced;
}
private static SSLSocketFactory disableSSLCertificatCheck()
throws KeyManagementException, NoSuchAlgorithmException {
// set a trust manager that just returns true for every request (this is _very_ unsafe and should only be used
// in the test environment)
TrustManager trustManager = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] certs, String authType) {
// do nothing, accept all clients
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
// do nothing accept all servers
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[] { trustManager }, SecureRandom.getInstanceStrong());
SSLSocketFactory replaced = HttpsURLConnection.getDefaultSSLSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
return replaced;
}
private static void restoreHostnameVerifier(HostnameVerifier toRestore) {
HttpsURLConnection.setDefaultHostnameVerifier(toRestore);
}
private static void restoreSocketFactory(SSLSocketFactory toRestore) {
HttpsURLConnection.setDefaultSSLSocketFactory(toRestore);
}
}