Fixing content negotiation to work with Tabulator.

This commit is contained in:
bdc34 2011-03-16 15:57:37 +00:00
parent cc1b8a2579
commit 740553e293
3 changed files with 205 additions and 33 deletions

View file

@ -81,7 +81,7 @@ public class IndividualController extends FreemarkerHttpServlet {
private static final String TEMPLATE_INDIVIDUAL_DEFAULT = "individual.ftl";
private static final String TEMPLATE_HELP = "individual-help.ftl";
private static Map<String,Float>qsMap;
@Override
protected ResponseValues processRequest(VitroRequest vreq) {
@ -486,42 +486,36 @@ public class IndividualController extends FreemarkerHttpServlet {
*/
protected ContentType checkForLinkedDataRequest(String url, VitroRequest vreq ) {
try {
ContentType contentType = null;
Matcher m;
// Check for url param specifying format
String formatParam = (String) vreq.getParameter("format");
if (formatParam != null) {
m = RDFXML_FORMAT.matcher(formatParam);
if ( m.matches() ) {
return new ContentType(RDFXML_MIMETYPE);
return ContentType.RDFXML;
}
m = N3_FORMAT.matcher(formatParam);
if( m.matches() ) {
return new ContentType(N3_MIMETYPE);
return ContentType.N3;
}
m = TTL_FORMAT.matcher(formatParam);
if( m.matches() ) {
return new ContentType(TTL_MIMETYPE);
return ContentType.TURTLE;
}
}
//check the accept header
String acceptHeader = vreq.getHeader("accept");
if (acceptHeader != null) {
List<ContentType> actualContentTypes = new ArrayList<ContentType>();
actualContentTypes.add(new ContentType( XHTML_MIMETYPE ));
actualContentTypes.add(new ContentType( HTML_MIMETYPE ));
actualContentTypes.add(new ContentType( RDFXML_MIMETYPE ));
actualContentTypes.add(new ContentType( N3_MIMETYPE ));
actualContentTypes.add(new ContentType( TTL_MIMETYPE ));
contentType = ContentType.getBestContentType(acceptHeader,actualContentTypes);
if (contentType!=null && (
RDFXML_MIMETYPE.equals(contentType.getMediaType()) ||
N3_MIMETYPE.equals(contentType.getMediaType()) ||
TTL_MIMETYPE.equals(contentType.getMediaType()) ))
return contentType;
if (acceptHeader != null) {
String ctStr = ContentType.getBestContentType(
ContentType.getTypesAndQ(acceptHeader),
getAcceptedContentTypes());
if (ctStr!=null && (
RDFXML_MIMETYPE.equals(ctStr) ||
N3_MIMETYPE.equals(ctStr) ||
TTL_MIMETYPE.equals(ctStr) ))
return new ContentType(ctStr);
}
/*
@ -532,15 +526,15 @@ public class IndividualController extends FreemarkerHttpServlet {
*/
m = RDF_REQUEST.matcher(url);
if( m.matches() ) {
return new ContentType(RDFXML_MIMETYPE);
return ContentType.RDFXML;
}
m = N3_REQUEST.matcher(url);
if( m.matches() ) {
return new ContentType(N3_MIMETYPE);
return ContentType.N3;
}
m = TTL_REQUEST.matcher(url);
if( m.matches() ) {
return new ContentType(TTL_MIMETYPE);
return ContentType.TURTLE;
}
@ -550,10 +544,6 @@ public class IndividualController extends FreemarkerHttpServlet {
return null;
}
private ContentType getContentTypeFromString(String string) {
return null;
}
@SuppressWarnings("unused")
private boolean checkForSunset(VitroRequest vreq, Individual entity) {
@ -750,4 +740,21 @@ public class IndividualController extends FreemarkerHttpServlet {
return new TemplateResponseValues(Template.TITLED_ERROR_MESSAGE.toString(), body);
}
public static Map<String, Float> getAcceptedContentTypes() {
if( qsMap == null ){
HashMap<String,Float> map = new HashMap<String,Float>();
map.put(HTML_MIMETYPE , 0.5f);
map.put(XHTML_MIMETYPE, 0.5f);
map.put("application/xml", 0.5f);
map.put(RDFXML_MIMETYPE, 1.0f);
map.put(N3_MIMETYPE, 1.0f);
map.put(TTL_MIMETYPE, 1.0f);
qsMap = map;
}
return qsMap;
}
// static String getAcceptedContentType(String acceptHeader,Map<String,Float>qs){
//
// }
}

View file

@ -16,15 +16,14 @@ package edu.cornell.mannlib.vitro.webapp.web;
*/
/*
* THIS CODE HAS BEEN MODIFIED:
* The members of the Vitro/VIVO project have modified this code. It has
* been modified from the version produced by Google Inc.
*
* The code in this file is from the Google data API project 1.40.3 on 2010-03-08.
* See full license from gdata at bottom of this file.
* The Vitro project only uses the method getBestContentType().
* See full license from gdata at bottom of this file.
*/
//package com.google.gdata.util;
//import com.google.gdata.client.Service;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
@ -215,6 +214,26 @@ public class ContentType implements Serializable {
*/
public static final ContentType ANY = new ContentType("*/*").lock();
/**
* A ContetType that describes RDF/XML.
* Added by Brian Caruso for VIVO.
*/
public final static ContentType RDFXML = new ContentType("application/rdf+xml").lock();
/**
* A ContetType that describes N3 RDF, this is unofficial and unregistered
* Added by Brian Caruso for VIVO.
*/
public final static ContentType N3 = new ContentType("text/n3").lock();
/**
* A ContetType that describes turtle RDF, this is unofficial and unregistered
* Added by Brian Caruso for VIVO.
*/
public final static ContentType TURTLE = new ContentType("text/turtle").lock();
/**
* Determines the best "Content-Type" header to use in a servlet response
* based on the "Accept" header from a servlet request.
@ -301,6 +320,81 @@ public class ContentType implements Serializable {
return null;
}
/**
* Gets the best content type based weighted q from client accept header and
* the server weighted q of the extent that the type conveys the resource.
*
* From suggestions by Tim Berners-Lee at http://www.w3.org/DesignIssues/Conneg
*
* @param clentAcceptsTypes types the client can accept with Q weights.
* @param serverTypes types the server can provide with Q weights.
* @return returns content type of best match or null if no match.
*/
public static String getBestContentType(
Map<String, Float> clientAcceptsTypes,
Map<String, Float> serverTypes) {
float maxQ = 0.0f;
String type = null;
for( String serverType: serverTypes.keySet()){
float serverQ = serverTypes.get(serverType);
Float clientQ = clientAcceptsTypes.get(serverType);
if( clientQ != null && ((serverQ * clientQ)+ 0.001) > (maxQ + 0.001) ){
maxQ = (serverQ * clientQ);
type = serverType;
}
}
return type;
}
/**
* This method was added by Brian Caruso of the VIVO project. March 15 2011.
*
* @param acceptHeader
* @return the types and the q values from the accept header
*/
public static Map<String,Float> getTypesAndQ(String acceptHeader){
if (acceptHeader == null) {
return Collections.emptyMap();
}
Map<String,Float> qcMap = new HashMap<String,Float>();
// iterate over all of the accepted content types
String[] acceptedTypes = acceptHeader.split(",");
for (String acceptedTypeString : acceptedTypes) {
// create the content type object
ContentType acceptedContentType;
try {
acceptedContentType = new ContentType(acceptedTypeString.trim());
} catch (IllegalArgumentException ex) {
// ignore exception
continue;
}
// parse the "q" value (default of 1)
float curQ = 1;
try {
String qAttr = acceptedContentType.getAttribute("q");
if (qAttr != null) {
float qValue = Float.valueOf(qAttr);
if (qValue <= 0 || qValue > 1) {
continue;
}
curQ = qValue + 0.0001F;
}
} catch (NumberFormatException ex) {
// ignore exception
continue;
}
if( acceptedContentType != null ){
qcMap.put(acceptedContentType.getMediaType(), curQ);
}
}
return qcMap;
}
/**
* Constructs a new instance with default media type
*/
@ -407,6 +501,8 @@ public class ContentType implements Serializable {
return sb.toString();
}
private Float q=1.0f;
private HashMap<String, String> attributes = new HashMap<String, String>();
/**
@ -445,6 +541,17 @@ public class ContentType implements Serializable {
return attributes.get(name);
}
/**
* returns q associated with content type.
*/
public float getQ(){
return q;
}
public void setQ(float q){
this.q = q;
}
/*
* Returns the charset attribute of the content type or null if the
* attribute has not been set.
@ -545,6 +652,7 @@ public class ContentType implements Serializable {
return (type.hashCode() * 31 + subType.hashCode()) * 31 + attributes
.hashCode();
}
}
/*

View file

@ -0,0 +1,57 @@
package edu.cornell.mannlib.vitro.webapp.web;
import java.util.Map;
import junit.framework.Assert;
import org.junit.Test;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.IndividualController;
public class ContentTypeTest {
@Test
public void typeAndQTest1(){
Map<String,Float> map = ContentType.getTypesAndQ(
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/rdf+xml;q=0.93,text/rdf+n3;q=0.5");
Assert.assertEquals(1.0f, map.get("text/html"), 0.01f);
Assert.assertEquals(1.0f, map.get("application/xhtml+xml"), 0.01f);
Assert.assertEquals(0.9f, map.get("application/xml"), 0.01f);
Assert.assertEquals(0.93f,map.get("application/rdf+xml"), 0.01f);
Assert.assertEquals(0.5f, map.get("text/rdf+n3"), 0.01f);
Assert.assertEquals(0.8f,map.get("*/*"), 0.01f);
}
@Test
public void typeAndQTest2(){
Map<String,Float> map = ContentType.getTypesAndQ(
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
Assert.assertEquals(1.0f, map.get("text/html"), 0.01f);
Assert.assertEquals(1.0f, map.get("application/xhtml+xml"), 0.01f);
Assert.assertEquals(0.9f, map.get("application/xml"), 0.01f);
Assert.assertEquals(0.8f,map.get("*/*"), 0.01f);
}
@Test
public void testWeightedBestContentTypeForTabulator(){
//accept header from tabulator
Map<String,Float> clientAccepts = ContentType.getTypesAndQ(
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/rdf+xml;q=0.93,text/rdf+n3;q=0.5");
Map<String,Float> serverTypes = IndividualController.getAcceptedContentTypes();
Assert.assertEquals("application/rdf+xml", ContentType.getBestContentType(clientAccepts, serverTypes));
}
@Test
public void testWeightedBestContentTypeForFirefox(){
//accept header from normal firefox
Map<String,Float> clientAccepts = ContentType.getTypesAndQ(
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
Map<String,Float> serverTypes = IndividualController.getAcceptedContentTypes();
Assert.assertEquals("application/xhtml+xml", ContentType.getBestContentType(clientAccepts, serverTypes));
}
}