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.http.commons.responses;
019
020import static java.util.stream.Stream.of;
021import static com.google.common.util.concurrent.Futures.addCallback;
022import static javax.ws.rs.core.MediaType.valueOf;
023import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDdateTime;
024import static org.apache.jena.graph.NodeFactory.createLiteral;
025import static org.apache.jena.graph.NodeFactory.createURI;
026import static org.apache.jena.graph.Triple.create;
027import static org.apache.jena.rdf.model.ModelFactory.createDefaultModel;
028import static org.apache.jena.rdf.model.ResourceFactory.createProperty;
029import static org.apache.jena.rdf.model.ResourceFactory.createResource;
030import static org.apache.jena.rdf.model.ResourceFactory.createTypedLiteral;
031import static org.fcrepo.http.commons.domain.RDFMediaType.TURTLE_TYPE;
032import static org.junit.Assert.assertTrue;
033import static org.mockito.Mockito.mock;
034import static org.slf4j.LoggerFactory.getLogger;
035
036import java.io.ByteArrayInputStream;
037import java.io.ByteArrayOutputStream;
038import java.io.IOException;
039import java.io.InputStream;
040import java.io.OutputStream;
041import java.util.HashMap;
042import java.util.Map;
043import java.util.stream.Stream;
044
045import javax.ws.rs.WebApplicationException;
046import javax.ws.rs.core.MediaType;
047
048import com.google.common.util.concurrent.FutureCallback;
049import com.google.common.util.concurrent.MoreExecutors;
050
051import org.apache.jena.rdf.model.RDFNode;
052import org.apache.jena.riot.RiotException;
053import org.fcrepo.kernel.api.rdf.DefaultRdfStream;
054import org.fcrepo.kernel.api.RdfStream;
055import org.junit.Before;
056import org.junit.Test;
057import org.mockito.Mock;
058import org.mockito.stubbing.Answer;
059import org.slf4j.Logger;
060
061import org.apache.jena.graph.Node;
062import org.apache.jena.graph.Triple;
063import org.apache.jena.rdf.model.Model;
064
065import org.junit.runner.RunWith;
066import org.mockito.junit.MockitoJUnitRunner;
067
068/**
069 * <p>RdfStreamStreamingOutputTest class.</p>
070 *
071 * @author ajs6f
072 */
073@RunWith(MockitoJUnitRunner.class)
074public class RdfStreamStreamingOutputTest {
075
076    private RdfStreamStreamingOutput testRdfStreamStreamingOutput;
077
078    private static final Triple triple = create(createURI("info:testSubject"),
079            createURI("info:testPredicate"), createURI("info:testObject"));
080
081    @Mock
082    private Node mockNode;
083
084    private final RdfStream testRdfStream = new DefaultRdfStream(triple.getSubject(), of(triple));
085
086    private final Map<String, String> testNamespaces = new HashMap<>();
087
088    @Mock
089    private RdfStream mockRdfStream;
090
091    private final MediaType testMediaType = valueOf("application/rdf+xml");
092
093    private static final Logger LOGGER =
094            getLogger(RdfStreamStreamingOutputTest.class);
095
096    @Before
097    public void setUp() {
098        testRdfStreamStreamingOutput =
099            new RdfStreamStreamingOutput(testRdfStream, testNamespaces, testMediaType);
100    }
101
102    @Test
103    public void testWrite() throws IOException {
104        assertOutputContainsTriple(triple);
105    }
106
107    public void assertOutputContainsTriple(final Triple expected) throws IOException {
108        try (final RdfStream input = new DefaultRdfStream(expected.getSubject(), of(expected));
109                final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
110            new RdfStreamStreamingOutput(input, testNamespaces, testMediaType).write(output);
111            try ( final InputStream resultStream = new ByteArrayInputStream(output.toByteArray())) {
112                final Model result = createDefaultModel().read(resultStream, null);
113                assertTrue("Didn't find our test triple!", result.contains(result.asStatement(expected)));
114            }
115        }
116    }
117
118    @Test
119    public void testWriteWithNamespace() throws IOException {
120        final Map<String, String> namespaces = new HashMap<>();
121        namespaces.put("a", "info:");
122        try (final RdfStream input = new DefaultRdfStream(triple.getSubject(), of(triple));
123                final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
124            new RdfStreamStreamingOutput(input, namespaces, TURTLE_TYPE).write(output);
125            final String s = output.toString("UTF-8");
126            assertTrue(s.replaceAll("\\s+", " ").contains("@prefix a: <info:>"));
127        }
128    }
129
130    @Test
131    public void testWriteWithTypedObject() throws IOException {
132        assertOutputContainsTriple(create(createURI("info:testSubject"),
133                createURI("info:testPredicate"),
134                createTypedLiteral(0).asNode()));
135    }
136
137    @Test
138    public void testWriteWithBlankSubject() throws IOException {
139        try (final RdfStream input = new DefaultRdfStream(createResource().asNode(), of(create(createResource()
140                .asNode(), createURI("info:testPredicate"), createTypedLiteral(0).asNode())));
141                final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
142            new RdfStreamStreamingOutput(input, testNamespaces, testMediaType).write(output);
143            try (final InputStream resultStream = new ByteArrayInputStream(output.toByteArray())) {
144                final Model result = createDefaultModel().read(resultStream, null);
145                assertTrue(result.contains(null, createProperty("info:testPredicate"), createTypedLiteral(0)));
146            }
147        }
148    }
149
150
151    @Test
152    public void testWriteWithBlankObject() throws IOException {
153        final Stream<Triple> triples =
154                of(create(createResource().asNode(), createURI("info:testPredicate"), createResource().asNode()));
155        try (final RdfStream input = new DefaultRdfStream(createResource().asNode(), triples);
156                final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
157            new RdfStreamStreamingOutput(input, testNamespaces, testMediaType).write(output);
158            try (final InputStream resultStream = new ByteArrayInputStream(output.toByteArray())) {
159                final Model result = createDefaultModel().read(resultStream, null);
160                assertTrue(result.contains(null, createProperty("info:testPredicate"), (RDFNode) null));
161            }
162        }
163    }
164
165    @Test
166    public void testWriteWithDatetimeObject() throws IOException {
167        assertOutputContainsTriple(create(createURI("info:testSubject"),
168                createURI("info:testPredicate"), createLiteral("2014-01-01T01:02:03Z", XSDdateTime)));
169    }
170
171    @Test
172    public void testWriteWithLanguageLiteral() throws IOException {
173        assertOutputContainsTriple(create(createURI("info:testSubject"),
174                createURI("info:testPredicate"),
175                createLiteral("french string", "fr")));
176    }
177
178    @Test(expected = WebApplicationException.class)
179    public void testWriteWithException() throws IOException {
180
181        final FutureCallback<Void> callback = new FutureCallback<Void>() {
182
183            @Override
184            public void onSuccess(final Void v) {
185                throw new AssertionError("Should never happen!");
186            }
187
188            @Override
189            public void onFailure(final Throwable e) {
190                LOGGER.debug("Got exception:", e.getMessage());
191                assertTrue("Got wrong kind of exception!", e instanceof RiotException);
192            }
193        };
194        addCallback(testRdfStreamStreamingOutput, callback, MoreExecutors.directExecutor());
195        try (final OutputStream mockOutputStream = mock(OutputStream.class, (Answer<Object>) invocation -> {
196            throw new RiotException("Expected.");
197        })) {
198            testRdfStreamStreamingOutput.write(mockOutputStream);
199        }
200    }
201}