001/**
002 * Copyright 2015 DuraSpace, Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.fcrepo.http.commons.responses;
017
018import static javax.ws.rs.core.MediaType.APPLICATION_XHTML_XML_TYPE;
019import static javax.ws.rs.core.MediaType.APPLICATION_XML;
020import static javax.ws.rs.core.MediaType.TEXT_HTML_TYPE;
021import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
022import static org.fcrepo.http.commons.domain.RDFMediaType.JSON_LD;
023import static org.fcrepo.http.commons.domain.RDFMediaType.N3;
024import static org.fcrepo.http.commons.domain.RDFMediaType.N3_ALT2;
025import static org.fcrepo.http.commons.domain.RDFMediaType.NTRIPLES;
026import static org.fcrepo.http.commons.domain.RDFMediaType.RDF_XML;
027import static org.fcrepo.http.commons.domain.RDFMediaType.TURTLE;
028import static org.fcrepo.http.commons.domain.RDFMediaType.TURTLE_X;
029import static org.openrdf.rio.RDFFormat.NO_CONTEXTS;
030import static org.openrdf.rio.RDFFormat.NO_NAMESPACES;
031import static org.slf4j.LoggerFactory.getLogger;
032
033import java.io.OutputStream;
034import java.lang.annotation.Annotation;
035import java.lang.reflect.Type;
036import java.nio.charset.Charset;
037
038import javax.annotation.PostConstruct;
039import javax.jcr.RepositoryException;
040import javax.ws.rs.Produces;
041import javax.ws.rs.WebApplicationException;
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.fcrepo.kernel.impl.rdf.impl.NamespaceRdfContext;
048import org.fcrepo.kernel.utils.iterators.RdfStream;
049import org.openrdf.rio.RDFFormat;
050import org.openrdf.rio.RDFWriterRegistry;
051import org.openrdf.rio.ntriples.NTriplesWriterFactory;
052import org.slf4j.Logger;
053
054/**
055 * Provides serialization for streaming RDF results.
056 *
057 * @author ajs6f
058 * @since Nov 19, 2013
059 */
060@Provider
061@Produces({TURTLE, N3, N3_ALT2, RDF_XML, NTRIPLES, APPLICATION_XML, TEXT_PLAIN, TURTLE_X, JSON_LD})
062public class RdfStreamProvider implements MessageBodyWriter<RdfStream> {
063
064    private static final Logger LOGGER = getLogger(RdfStreamProvider.class);
065
066    @Override
067    public boolean isWriteable(final Class<?> type, final Type genericType,
068            final Annotation[] annotations, final MediaType mediaType) {
069        LOGGER.debug(
070                "Checking to see if we can serialize type: {} to mimeType: {}",
071                type.getName(), mediaType.toString());
072        if (!RdfStream.class.isAssignableFrom(type)) {
073            return false;
074        }
075        if (mediaType.equals(TEXT_HTML_TYPE)
076                || mediaType.equals(APPLICATION_XHTML_XML_TYPE)
077                || (mediaType.getType().equals("application") && mediaType
078                        .getSubtype().equals("html"))) {
079            LOGGER.debug("Was asked for an HTML mimeType, returning false.");
080            return false;
081        }
082        LOGGER.debug("Assuming that this is an attempt to retrieve RDF, returning true.");
083        return true;
084    }
085
086    @Override
087    public long getSize(final RdfStream t, final Class<?> type,
088            final Type genericType, final Annotation[] annotations,
089            final MediaType mediaType) {
090        // We do not know how long the stream is
091        return -1;
092    }
093
094    @Override
095    public void writeTo(final RdfStream rdfStream, final Class<?> type,
096        final Type genericType, final Annotation[] annotations,
097        final MediaType mediaType,
098        final MultivaluedMap<String, Object> httpHeaders,
099        final OutputStream entityStream) {
100
101        LOGGER.debug("Serializing an RdfStream to mimeType: {}", mediaType);
102        try {
103            if (rdfStream.namespaces().isEmpty()) {
104                final RdfStream namespaceRdfContext = new NamespaceRdfContext(rdfStream.session());
105                rdfStream.namespaces(namespaceRdfContext.namespaces());
106            }
107
108            final RdfStreamStreamingOutput streamOutput = new RdfStreamStreamingOutput(rdfStream, mediaType);
109            streamOutput.write(entityStream);
110        } catch (final RepositoryException e) {
111            throw new WebApplicationException(e);
112        }
113
114
115    }
116
117    /**
118     * Add the correct mimeType for n-triples.
119     */
120    @PostConstruct
121    public void registerMimeTypes() {
122        RDFWriterRegistry.getInstance().add(new NTriplesWithCorrectMimeType());
123    }
124
125    /**
126     * OpenRDF uses the wrong mimeType for n-triples, so we offer the correct
127     * one as well.
128     *
129     * @author ajs6f
130     * @since Nov 20, 2013
131     */
132    public static class NTriplesWithCorrectMimeType extends
133        NTriplesWriterFactory {
134
135        private static final RDFFormat NTRIPLESWITHCORRECTMIMETYPE =
136            new RDFFormat("N-Triples-with-correct-mimeType",
137                    NTRIPLES, Charset.forName("US-ASCII"), "nt",
138                    NO_NAMESPACES, NO_CONTEXTS);
139
140        @Override
141        public RDFFormat getRDFFormat() {
142            return NTRIPLESWITHCORRECTMIMETYPE;
143        }
144    }
145
146}