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