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