org.dspace.rdf.negotiation.Negotiator.java Source code

Java tutorial

Introduction

Here is the source code for org.dspace.rdf.negotiation.Negotiator.java

Source

/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 * http://www.dspace.org/license/
 */
package org.dspace.rdf.negotiation;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.dspace.rdf.RDFUtil;
import org.dspace.services.factory.DSpaceServicesFactory;

/**
 *
 * @author Pascal-Nicolas Becker (dspace -at- pascal -hyphen- becker -dot- de)
 */
public class Negotiator {

    // Serialiazation codes
    public static final int UNSPECIFIED = -1;
    public static final int WILDCARD = 0;
    public static final int HTML = 1;
    public static final int RDFXML = 2;
    public static final int TURTLE = 3;
    public static final int N3 = 4;

    public static final String DEFAULT_LANG = "html";

    private static final Logger log = Logger.getLogger(Negotiator.class);

    public static int negotiate(String acceptHeader) {
        if (acceptHeader == null)
            return UNSPECIFIED;

        String[] mediaRangeSpecs = acceptHeader.split(",");
        ArrayList<MediaRange> requestedMediaRanges = new ArrayList<>();
        for (String mediaRangeSpec : mediaRangeSpecs) {
            try {
                requestedMediaRanges.add(new MediaRange(mediaRangeSpec));
            } catch (IllegalArgumentException | IllegalStateException ex) {
                log.warn("Couldn't parse part of an AcceptHeader, ignoring it.\n" + ex.getMessage(), ex);
            }
        }
        if (requestedMediaRanges.isEmpty()) {
            return UNSPECIFIED;
        }

        Collections.sort(requestedMediaRanges, getMediaRangeComparator());
        Collections.reverse(requestedMediaRanges);

        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder("Parsed Accept header '" + acceptHeader + "':\n");
            for (Iterator<MediaRange> it = requestedMediaRanges.iterator(); it.hasNext();) {
                MediaRange mr = it.next();
                sb.append(mr.getType()).append("/").append(mr.getSubtype());
                sb.append(" has a qvalue of ").append(Double.toString(mr.getQvalue()));
                sb.append("\n");
            }
            log.debug(sb.toString());
        }

        boolean wildcard = false;
        boolean html = false;
        boolean rdf = false;
        boolean n3 = false;
        boolean turtle = false;
        Iterator<MediaRange> it = requestedMediaRanges.iterator();

        MediaRange lookahead = it.hasNext() ? it.next() : null;
        while (lookahead != null) {
            double qvalue = lookahead.getQvalue();
            String type = lookahead.getType();
            String subtype = lookahead.getSubtype();

            lookahead = it.hasNext() ? it.next() : null;

            if (qvalue <= 0.0) {
                // a quality of 0.0 means that the defined media range should 
                // not to be send => don't parse it.
                continue;
            }

            if ("*".equals(type)) {
                wildcard = true;
            }
            if (("text".equals(type) && "html".equals(subtype))
                    || ("application".equals(type) && "xhtml+xml".equals(subtype))) {
                html = true;
            }
            if ("application".equals(type) && "rdf+xml".equals(subtype)) {
                rdf = true;
            }
            if (("text".equals(type) && "n3".equals(subtype)) || ("text".equals(type) && "rdf+n3".equals(subtype))
                    || ("application".equals(type) && "n3".equals(subtype))) {
                n3 = true;
            }
            if (("text".equals(type) && "turtle".equals(subtype))
                    || ("application".equals(type) && "turtle".equals(subtype))
                    || ("application".equals(type) && "x-turtle".equals(subtype))
                    || ("application".equals(type) && "rdf+turtle".equals(subtype))) {
                turtle = true;
            }

            if (lookahead != null && qvalue != lookahead.qvalue && (wildcard || html || rdf || n3 || turtle)) {
                // we've looked over all media range with the same precedence
                // and found one, we can serve
                break;
            }
        }

        if (html) {
            return HTML;
        }
        if (wildcard) {
            return WILDCARD;
        } else if (turtle) {
            return TURTLE;
        } else if (n3) {
            return N3;
        } else if (rdf) {
            return RDFXML;
        }

        return UNSPECIFIED;
    }

    /**
     * Method to get a comparator to compare media ranges regarding their 
     * content negotiation precedence. Following RFC 2616 a media range is 
     * higher prioritized then another media range if the first one has a higher 
     * quality value then the second. If both quality values are equal, the 
     * media range that is more specific should be used.
     * 
     * <p>Note: this comparator imposes orderings that are inconsistent with 
     * equals! Caution should be exercised when using it to order a sorted set
     * or a sorted map. Take a look at the java.util.Comparator for further 
     * information.</p>
     * @return A comparator that imposes orderings that are inconsistent with equals!
     */
    public static Comparator<MediaRange> getMediaRangeComparator() {
        return new Comparator<MediaRange>() {

            @Override
            public int compare(MediaRange mr1, MediaRange mr2) {
                if (Double.compare(mr1.qvalue, mr2.getQvalue()) != 0) {
                    return Double.compare(mr1.qvalue, mr2.getQvalue());
                }
                if (mr1.typeIsWildcard() && mr2.typeIsWildcard())
                    return 0;
                if (mr1.typeIsWildcard() && !mr2.typeIsWildcard())
                    return -1;
                if (!mr1.typeIsWildcard() && mr2.typeIsWildcard())
                    return 1;
                if (mr1.subtypeIsWildcard() && mr2.subtypeIsWildcard())
                    return 0;
                if (mr1.subtypeIsWildcard() && !mr2.subtypeIsWildcard())
                    return -1;
                if (!mr1.subtypeIsWildcard() && mr2.subtypeIsWildcard())
                    return 1;

                // if the quality of two media ranges is equal and both don't 
                // use an asterisk either as type or subtype, they are equal in 
                // the sense of content negotiation precedence.
                return 0;
            }
        };
    }

    public static boolean sendRedirect(HttpServletResponse response, String handle, String extraPathInfo,
            int serialization, boolean redirectHTML) throws IOException {
        if (extraPathInfo == null)
            extraPathInfo = "";

        StringBuilder urlBuilder = new StringBuilder();
        String lang = null;
        switch (serialization) {
        case (Negotiator.UNSPECIFIED):
        case (Negotiator.WILDCARD): {
            lang = DEFAULT_LANG;
            break;
        }
        case (Negotiator.HTML): {
            lang = "html";
            break;
        }
        case (Negotiator.RDFXML): {
            lang = "rdf";
            break;
        }
        case (Negotiator.TURTLE): {
            lang = "turtle";
            break;
        }
        case (Negotiator.N3): {
            lang = "n3";
            break;
        }
        default: {
            lang = DEFAULT_LANG;
            break;
        }
        }
        assert (lang != null);

        if (StringUtils.isEmpty(handle)) {
            log.warn("Handle is empty, set it to Site Handle.");
            handle = DSpaceServicesFactory.getInstance().getConfigurationService().getProperty("handle.prefix")
                    + "/0";
        }

        // don't redirect if HTML is requested and content negotiation is done
        // in a ServletFilter, as the ServletFilter should just let the request
        // pass.
        if ("html".equals(lang) && !redirectHTML) {
            return false;
        }

        // as we do content negotiation and we'll redirect the request, we 
        // should send a vary caching so browsers can adopt their caching strategy
        response.setHeader("Vary", "Accept");

        // if html is requested we have to forward to the repositories webui.
        if ("html".equals(lang)) {
            urlBuilder.append(
                    DSpaceServicesFactory.getInstance().getConfigurationService().getProperty("dspace.url"));
            if (!handle.equals(
                    DSpaceServicesFactory.getInstance().getConfigurationService().getProperty("handle.prefix")
                            + "/0")) {
                urlBuilder.append("/handle/");
                urlBuilder.append(handle).append("/").append(extraPathInfo);
            }
            String url = urlBuilder.toString();

            log.debug("Will forward to '" + url + "'.");
            response.setStatus(HttpServletResponse.SC_SEE_OTHER);
            response.setHeader("Location", url);
            response.flushBuffer();
            return true;
        }

        // currently we cannot serve statistics as rdf
        if ("statistics".equals(extraPathInfo)) {
            log.info("Cannot send statistics as RDF yet. => 406 Not Acceptable.");
            response.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE);
            response.flushBuffer();
            return true;
        }

        // load the URI of the dspace-rdf module.
        urlBuilder.append(DSpaceServicesFactory.getInstance().getConfigurationService()
                .getProperty(RDFUtil.CONTEXT_PATH_KEY));
        if (urlBuilder.length() == 0) {
            log.error("Cannot load URL of dspace-rdf module. " + "=> 500 Internal Server Error");
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            response.flushBuffer();
            return true;
        }
        // and build the uri to the DataProviderServlet
        urlBuilder.append("/handle/").append(handle);
        urlBuilder.append("/").append(lang);
        String url = urlBuilder.toString();
        log.debug("Will forward to '" + url + "'.");
        response.setStatus(HttpServletResponse.SC_SEE_OTHER);
        response.setHeader("Location", url);
        response.flushBuffer();
        return true;
    }
}