org.apache.jena.fuseki.conneg.ConNeg Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jena.fuseki.conneg;
import static org.apache.jena.fuseki.HttpNames.hAcceptCharset ;
import javax.servlet.http.HttpServletRequest ;
import org.apache.jena.atlas.web.AcceptList ;
import org.apache.jena.atlas.web.MediaRange ;
import org.apache.jena.atlas.web.MediaType ;
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
/**
* Content negotiation is a mechanism defined in the HTTP specification
* that makes it possible to serve different versions of a document
* (or more generally, a resource representation) at the same URI, so that
* user agents can specify which version fit their capabilities the best.
*
* ConNeg is used in Fuseki to help matching the content media type requested
* by the user, against the list of offered media types.
*
* @see http://en.wikipedia.org/wiki/Content_negotiation
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
*/
public class ConNeg
{
private static Logger log = LoggerFactory.getLogger(ConNeg.class) ;
// See riot.ContentNeg (client side).
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
/**
* Parses the content type. It splits the string by semi-colon and finds the
* other features such as the "q" quality factor. For a complete documentation
* on how the parsing happens, see
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1.
*
* @param contentType content type string
* @return parsed media type
*/
static public MediaType parse(String contentType)
{
try {
return MediaType.create(contentType) ;
} catch (RuntimeException ex) { return null ; }
}
/**
* Creates a {@link AcceptList} with the given HTTP header string and
* uses the {@link AcceptList#match(MediaType)} method to decide which
* media type matches the HTTP header string.
*
* The q quality factor is used to decide which choice is the best
* match.
*
* @param headerString HTTP header string
* @param offerList accept list
* @return matched media type
*/
static public MediaType match(String headerString, AcceptList offerList)
{
AcceptList l = new AcceptList(headerString) ;
return AcceptList.match(l, offerList) ;
}
/**
* Match a single media type against a header string.
*
* @param headerString HTTP header string
* @param mediaRangeStr Semi-colon separated list of media types
* @return the matched media type or null
if there was no match
*/
public static String match(String headerString, String mediaRangeStr)
{
AcceptList l = new AcceptList(headerString) ;
MediaRange aItem = new MediaRange(mediaRangeStr) ; // MediaType
MediaType m = l.match(aItem) ;
if ( m == null )
return null ;
return m.toHeaderString() ;
}
/**
* Split and trims a string using a given regex.
*
* @param s string
* @param splitStr given regex
* @return an array with the trimmed strings found
*/
/*package*/ static String[] split(String s, String splitStr)
{
String[] x = s.split(splitStr,2) ;
for ( int i = 0 ; i < x.length ; i++ )
{
x[i] = x[i].trim() ;
}
return x ;
}
/**
* Chooses the charset by using the Accept-Charset HTTP header.
*
* See {@link ConNeg#choose(String, AcceptList, MediaType)}.
*
* @param httpRequest HTTP request
* @param myPrefs accept list
* @param defaultMediaType default media type
* @return media type chosen
*/
public static MediaType chooseCharset(HttpServletRequest httpRequest,
AcceptList myPrefs,
MediaType defaultMediaType)
{
String a = httpRequest.getHeader(hAcceptCharset) ;
if ( log.isDebugEnabled() )
log.debug("Accept-Charset request: "+a) ;
MediaType item = choose(a, myPrefs, defaultMediaType) ;
if ( log.isDebugEnabled() )
log.debug("Charset chosen: "+item) ;
return item ;
}
/**
* Choose the content media type by extracting the Accept HTTP header from
* the HTTP request and choosing
* (see {@link ConNeg#choose(String, AcceptList, MediaType)}) a content media
* type that matches the header.
*
* @param httpRequest HTTP request
* @param myPrefs accept list
* @param defaultMediaType default media type
* @return media type chosen
*/
public static MediaType chooseContentType(HttpServletRequest httpRequest,
AcceptList myPrefs,
MediaType defaultMediaType)
{
String a = WebLib.getAccept(httpRequest) ;
if ( log.isDebugEnabled() )
log.debug("Accept request: "+a) ;
MediaType item = choose(a, myPrefs, defaultMediaType) ;
if ( log.isDebugEnabled() )
log.debug("Content type chosen: "+item) ;
return item ;
}
/**
* This method receives a HTTP header string, an {@link AcceptList} and a
* default {@link MediaType}.
*
* If the header string is null, it returns the given default MediaType.
*
* Otherwise it builds an {@link AcceptList} object with the header string
* and uses it to match against the given MediaType.
*
* @param headerString HTTP header string
* @param myPrefs accept list
* @param defaultMediaType default media type
* @return a media type or null
if none matched or if the
* HTTP header string and the default media type are null
.
*/
private static MediaType choose(String headerString, AcceptList myPrefs,
MediaType defaultMediaType)
{
if ( headerString == null )
return defaultMediaType ;
AcceptList headerList = new AcceptList(headerString) ;
if ( myPrefs == null )
{
MediaType i = headerList.first() ;
if ( i == null ) return defaultMediaType ;
return i ;
}
MediaType i = AcceptList.match(headerList, myPrefs) ;
if ( i == null )
return defaultMediaType ;
return i ;
}
}