001/*
002 * The contents of this file are subject to the license and copyright
003 * detailed in the LICENSE and NOTICE files at the root of the source
004 * tree.
005 */
006package org.fcrepo.integration.rdf;
007
008import org.apache.jena.datatypes.xsd.XSDDatatype;
009import org.apache.jena.graph.Graph;
010import org.apache.jena.graph.Node;
011import org.apache.jena.graph.Triple;
012import org.apache.jena.query.Dataset;
013import org.apache.jena.rdf.model.Model;
014import org.apache.jena.rdf.model.ModelFactory;
015import org.apache.jena.sparql.core.DatasetGraph;
016import org.apache.jena.sparql.graph.GraphFactory;
017import org.apache.jena.util.iterator.ExtendedIterator;
018import org.apache.commons.io.IOUtils;
019import org.apache.http.HttpResponse;
020import org.apache.http.client.methods.HttpGet;
021import org.apache.http.client.methods.HttpPut;
022import org.apache.http.entity.BasicHttpEntity;
023import org.apache.jena.riot.RDFDataMgr;
024import org.apache.jena.riot.RDFLanguages;
025import org.fcrepo.integration.http.api.AbstractResourceIT;
026
027import javax.ws.rs.core.Response;
028import java.io.ByteArrayOutputStream;
029import java.io.IOException;
030import java.net.URI;
031import java.util.HashMap;
032import java.util.Map;
033
034import static java.nio.charset.StandardCharsets.UTF_8;
035import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE;
036import static javax.ws.rs.core.Response.Status.CREATED;
037import static org.apache.jena.graph.NodeFactory.createBlankNode;
038import static org.apache.jena.graph.NodeFactory.createLiteral;
039import static org.junit.Assert.assertEquals;
040import static org.junit.Assert.assertFalse;
041import static org.junit.Assert.assertTrue;
042
043/**
044 * @author cabeer
045 * @author ajs6f
046 */
047public abstract class AbstractIntegrationRdfIT extends AbstractResourceIT {
048
049    protected HttpResponse createLDPRSAndCheckResponse(final String pid, final String body) {
050        return createLDPRSAndCheckResponse(pid, body, null);
051    }
052
053    protected HttpResponse createLDPRSAndCheckResponse(final String pid, final String body,
054            final Map<String, String> headers) {
055        try {
056            final HttpPut httpPut = new HttpPut(serverAddress + pid);
057            if (headers != null && !headers.isEmpty()) {
058                headers.keySet().stream().forEach(k -> httpPut.addHeader(k, headers.get(k)));
059            }
060            if (httpPut.getFirstHeader(CONTENT_TYPE) == null) {
061                httpPut.addHeader(CONTENT_TYPE, "text/turtle");
062            }
063            httpPut.setHeader("Slug", pid);
064            final BasicHttpEntity e = new BasicHttpEntity();
065            e.setContent(IOUtils.toInputStream(body, UTF_8));
066            httpPut.setEntity(e);
067            final HttpResponse response = client.execute(httpPut);
068            checkResponse(response, CREATED);
069
070            final String location = response.getFirstHeader("Location").getValue();
071
072            final HttpGet httpGet = new HttpGet(location);
073            httpGet.addHeader("Prefer", "return=representation; " +
074                    "include=\"http://www.w3.org/ns/ldp#PreferMinimalContainer\"; " +
075                    "omit=\"http://fedora.info/definitions/v4/repository#ServerManaged\"");
076            final Dataset dataset = getDataset(httpGet);
077
078            final DatasetGraph graphStore = dataset.asDatasetGraph();
079            assertFalse(graphStore.isEmpty());
080
081            final Graph tidiedGraph = getTidiedGraph(graphStore);
082            final Model expected = ModelFactory.createDefaultModel().read(
083                    IOUtils.toInputStream(body, UTF_8), location, "TTL");
084
085            final boolean isomorphicWith = tidiedGraph.isIsomorphicWith(getTidiedGraph(expected.getGraph()));
086
087            final String description;
088
089            if (!isomorphicWith) {
090                final ByteArrayOutputStream o = new ByteArrayOutputStream();
091
092                final Model tidiedModel = ModelFactory.createModelForGraph(tidiedGraph);
093                tidiedModel.setNsPrefixes(expected.getNsPrefixMap());
094                o.write("Expected: ".getBytes());
095                RDFDataMgr.write(o, expected, RDFLanguages.TTL);
096                o.write("to be isomorphic with: ".getBytes());
097                RDFDataMgr.write(o, tidiedModel, RDFLanguages.TTL);
098                description = IOUtils.toString(o.toByteArray(), "UTF-8");
099            } else {
100                description = "";
101            }
102
103
104            assertTrue(description, isomorphicWith);
105
106            return response;
107        } catch (final IOException e) {
108            assertTrue("Got IOException " + e, false);
109            return null;
110        }
111    }
112
113    private static Graph getTidiedGraph(final DatasetGraph graph) {
114        return getTidiedGraph(graph.getDefaultGraph());
115    }
116
117    private static Graph getTidiedGraph(final Graph graph) {
118        final Graph betterGraph = GraphFactory.createDefaultGraph();
119        final ExtendedIterator<Triple> triples = graph.find(Node.ANY, Node.ANY, Node.ANY);
120        final Map<Node, Node> bnodeMap = new HashMap<>();
121
122        while (triples.hasNext()) {
123            final Triple next = triples.next();
124
125            Triple replacement = next;
126
127            if (isSkolemizedBnode(replacement.getSubject())) {
128                if (!bnodeMap.containsKey(replacement.getSubject())) {
129                    bnodeMap.put(replacement.getSubject(), createBlankNode());
130                }
131
132                replacement = new Triple(bnodeMap.get(replacement.getSubject()),
133                        replacement.getPredicate(),
134                        replacement.getObject());
135            }
136
137            if (isSkolemizedBnode(replacement.getObject())) {
138
139                if (!bnodeMap.containsKey(replacement.getObject())) {
140                    bnodeMap.put(replacement.getObject(), createBlankNode());
141                }
142
143                replacement = new Triple(replacement.getSubject(),
144                        replacement.getPredicate(),
145                        bnodeMap.get(replacement.getObject()));
146            }
147
148            if (replacement.getObject().isLiteral()
149                    && replacement.getObject().getLiteral().getDatatype() != null
150                    && replacement.getObject().getLiteral().getDatatype().equals(XSDDatatype.XSDstring)) {
151                replacement = new Triple(replacement.getSubject(),
152                        replacement.getPredicate(),
153                        createLiteral(replacement.getObject().getLiteral().getLexicalForm()));
154            }
155
156            betterGraph.add(replacement);
157        }
158        return betterGraph;
159    }
160
161    private static boolean isSkolemizedBnode(final Node node) {
162        if (!node.isURI()) {
163            return false;
164        }
165        final URI uri = URI.create(node.toString());
166        return uri.getFragment() != null && uri.getFragment().startsWith("genid");
167    }
168
169    protected void checkResponse(final HttpResponse response, final Response.StatusType expected) {
170        final int actual = response.getStatusLine().getStatusCode();
171        assertEquals("Didn't get a CREATED response!", expected.getStatusCode(), actual);
172    }
173
174    protected String getContentFromClasspath(final String path) throws IOException {
175        return IOUtils.toString(this.getClass().getResourceAsStream(path), UTF_8);
176    }
177
178
179}