org.dspace.rdf.negotiation.Negotiator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dspace-api Show documentation
Show all versions of dspace-api Show documentation
DSpace core data model and service APIs.
/**
* 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.lang3.StringUtils;
import org.apache.commons.validator.routines.UrlValidator;
import org.apache.logging.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 = org.apache.logging.log4j.LogManager.getLogger(Negotiator.class);
/**
* Default constructor
*/
private Negotiator() { }
public static int negotiate(String acceptHeader) {
if (acceptHeader == null) {
return UNSPECIFIED;
}
String[] mediaRangeSpecs = acceptHeader.split(",");
ArrayList 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 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 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.
*
* 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.
*
* @return A comparator that imposes orderings that are inconsistent with equals!
*/
public static Comparator getMediaRangeComparator() {
return new Comparator() {
@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 = "";
}
UrlValidator urlValidator = new UrlValidator(UrlValidator.ALLOW_LOCAL_URLS);
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.ui.url"));
if (!handle.equals(DSpaceServicesFactory.getInstance()
.getConfigurationService().getProperty("handle.prefix") + "/0")) {
urlBuilder.append("/handle/");
urlBuilder.append(handle).append("/").append(extraPathInfo);
}
String url = urlBuilder.toString();
if (urlValidator.isValid(url)) {
log.debug("Will forward to '" + url + "'.");
response.setStatus(HttpServletResponse.SC_SEE_OTHER);
response.setHeader("Location", url);
response.flushBuffer();
return true;
} else {
throw new IOException("Invalid URL '" + url + "', cannot redirect.");
}
}
// 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();
if (urlValidator.isValid(url)) {
log.debug("Will forward to '" + url + "'.");
response.setStatus(HttpServletResponse.SC_SEE_OTHER);
response.setHeader("Location", url);
response.flushBuffer();
return true;
} else {
throw new IOException("Invalid URL '" + url + "', cannot redirect.");
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy