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 javax.ws.rs.core.MediaType.APPLICATION_XHTML_XML_TYPE;
021import static javax.ws.rs.core.MediaType.APPLICATION_XML;
022import static javax.ws.rs.core.MediaType.TEXT_HTML_TYPE;
023import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
024import static org.fcrepo.http.commons.domain.RDFMediaType.JSON_LD;
025import static org.fcrepo.http.commons.domain.RDFMediaType.N3;
026import static org.fcrepo.http.commons.domain.RDFMediaType.N3_ALT2;
027import static org.fcrepo.http.commons.domain.RDFMediaType.NTRIPLES;
028import static org.fcrepo.http.commons.domain.RDFMediaType.RDF_XML;
029import static org.fcrepo.http.commons.domain.RDFMediaType.TURTLE;
030import static org.fcrepo.http.commons.domain.RDFMediaType.TURTLE_X;
031import static org.openrdf.rio.RDFFormat.NO_CONTEXTS;
032import static org.openrdf.rio.RDFFormat.NO_NAMESPACES;
033import static org.slf4j.LoggerFactory.getLogger;
034
035import java.io.OutputStream;
036import java.lang.annotation.Annotation;
037import java.lang.reflect.Type;
038import java.nio.charset.Charset;
039
040import javax.annotation.PostConstruct;
041import javax.ws.rs.Produces;
042import javax.ws.rs.core.MediaType;
043import javax.ws.rs.core.MultivaluedMap;
044import javax.ws.rs.ext.MessageBodyWriter;
045import javax.ws.rs.ext.Provider;
046
047import org.openrdf.rio.RDFFormat;
048import org.openrdf.rio.RDFWriterRegistry;
049import org.openrdf.rio.ntriples.NTriplesWriterFactory;
050import org.slf4j.Logger;
051
052/**
053 * Provides serialization for streaming RDF results.
054 *
055 * @author ajs6f
056 * @since Nov 19, 2013
057 */
058@Provider
059@Produces({TURTLE, N3, N3_ALT2, RDF_XML, NTRIPLES, APPLICATION_XML, TEXT_PLAIN, TURTLE_X, JSON_LD})
060public class RdfStreamProvider implements MessageBodyWriter<RdfNamespacedStream> {
061
062    private static final Logger LOGGER = getLogger(RdfStreamProvider.class);
063
064    @Override
065    public boolean isWriteable(final Class<?> type, final Type genericType,
066            final Annotation[] annotations, final MediaType mediaType) {
067        LOGGER.debug(
068                "Checking to see if we can serialize type: {} to mimeType: {}",
069                type.getName(), mediaType.toString());
070        if (!RdfNamespacedStream.class.isAssignableFrom(type)) {
071            return false;
072        }
073        if (mediaType.equals(TEXT_HTML_TYPE)
074                || mediaType.equals(APPLICATION_XHTML_XML_TYPE)
075                || (mediaType.getType().equals("application") && mediaType
076                        .getSubtype().equals("html"))) {
077            LOGGER.debug("Was asked for an HTML mimeType, returning false.");
078            return false;
079        }
080        LOGGER.debug("Assuming that this is an attempt to retrieve RDF, returning true.");
081        return true;
082    }
083
084    @Override
085    public long getSize(final RdfNamespacedStream t, final Class<?> type,
086            final Type genericType, final Annotation[] annotations,
087            final MediaType mediaType) {
088        // We do not know how long the stream is
089        return -1;
090    }
091
092    @Override
093    public void writeTo(final RdfNamespacedStream nsStream, final Class<?> type,
094        final Type genericType, final Annotation[] annotations,
095        final MediaType mediaType,
096        final MultivaluedMap<String, Object> httpHeaders,
097        final OutputStream entityStream) {
098
099        LOGGER.debug("Serializing an RdfStream to mimeType: {}", mediaType);
100        final RdfStreamStreamingOutput streamOutput = new RdfStreamStreamingOutput(nsStream.stream,
101                nsStream.namespaces, mediaType);
102        streamOutput.write(entityStream);
103    }
104
105    /**
106     * Add the correct mimeType for n-triples.
107     */
108    @PostConstruct
109    public void registerMimeTypes() {
110        RDFWriterRegistry.getInstance().add(new NTriplesWithCorrectMimeType());
111    }
112
113    /**
114     * OpenRDF uses the wrong mimeType for n-triples, so we offer the correct
115     * one as well.
116     *
117     * @author ajs6f
118     * @since Nov 20, 2013
119     */
120    public static class NTriplesWithCorrectMimeType extends
121        NTriplesWriterFactory {
122
123        private static final RDFFormat NTRIPLESWITHCORRECTMIMETYPE =
124            new RDFFormat("N-Triples-with-correct-mimeType",
125                    NTRIPLES, Charset.forName("US-ASCII"), "nt",
126                    NO_NAMESPACES, NO_CONTEXTS);
127
128        @Override
129        public RDFFormat getRDFFormat() {
130            return NTRIPLESWITHCORRECTMIMETYPE;
131        }
132    }
133
134}