- From: Roland Mainz <Roland.Mainz@informatik.med.uni-giessen.de>
- Date: Sat, 17 Apr 1999 01:07:19 +0200
- To: W3 Jigsaw Mailinglist <www-jigsaw@w3.org>, "jigsaw@w3.org" <jigsaw@w3.org>
- Message-Id: <3717C2A7.64576ECC@informatik.med.uni-giessen.de>
Hi ! ---- Today I figured out a problem as I tried to use a NegotiatedFrame for switching between text/xml and text/html. Problem is that Netscape V4.51 adds */* at the end of the "accept"-header line, and text/html is completely missing in the "accept"-header line. Therefore, I wrote a V1.22 of NegotiatedFrame.java, which fixes the problem in a hack way (but it works now). I've added the source as an attachment. ---- I've started some "cosmetics" in the Multiple-Choice output of NegotiatedFrame, but it is not finished yet, sorry (1:02h Saturday now), maybe someone else has time to finish this... ---- Bye, Roland -- __ . . __ (o.\ \/ /.o) Roland Mainz C programmer \__\/\/__/ Roland.Mainz@informatik.med.uni-giessen.de MPEG specialist /O /==\ O\ gisburn@w-specht.rhein-ruhr.de Sun&&Amiga programmer (;O/ \/ \O;) TEL +49 (0) 2426901568 FAX +49 (0) 2426901569
// NegotiatedFrame.java
// $Id: NegotiatedFrame.java,v 1.22 1999/02/09 23:11:39 gisburn Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html
// new for V1.22:
// - */* in "Accept"-header is now replaced by text/html to solve a problem...
// - multiple choice page now gets a style sheed added;
//
// note to Yves/Benoit: I'm too tired (0:54h Saturday) to to the rest, but it would be nice
// if the multiple-choice page looks like the "normal" directory listing.
package org.w3c.jigsaw.frames;
import java.io.*;
import java.util.*;
import org.w3c.tools.resources.*;
import org.w3c.jigsaw.http.* ;
import org.w3c.jigsaw.html.*;
import org.w3c.jigsaw.html.HtmlGenerator ;
import org.w3c.www.mime.* ;
import org.w3c.www.http.*;
import org.w3c.tools.resources.ProtocolException;
import org.w3c.tools.resources.ResourceException;
/**
* Content negotiation frame.
* Selects between variants of the same "thing" in different formats based
* on information from the requesting client and variant attributes like language,
* content-type or content-encoding.
* @author Yves Lafon <ylafon@w3.org>
* @author Benoît Mahé <bmahe@w3.org>
* @author Roland Mainz <Roland.Mainz@informatik.med.uni-giessen.de>
*/
public class NegotiatedFrame extends HTTPFrame {
class VariantState {
ResourceReference variant = null ;
double qs = 0.0 ;
double qe = 0.0 ;
double qc = 0.0 ;
double ql = 0.0 ;
double q = 0.0 ; // quality (mime type one)
double Q = 0.0 ; // the big Q
public String toString() {
try {
Resource res = variant.lock();
String name = (String) res.getIdentifier() ;
if ( name == null )
name = "<noname>" ;
return "[" + name
+ " qs=" + qs
+ " qe=" + qe
+ " ql=" + ql
+ " q =" + q
+ " Q =" + getQ()
+"]" ;
} catch (InvalidResourceException ex) {
return "invalid";
} finally {
variant.unlock();
}
}
void setContentEncodingQuality (double qe) {
this.qe = qe ;
}
void setContentEncodingQuality (HttpAcceptEncoding e) {
this.qe = e.getQuality();
}
double getContentEncodingQuality () {
return qe ;
}
void setQuality (double q) {
this.q = q ;
}
void setQuality (HttpAccept a) {
q = a.getQuality() ;
}
void setLanguageQuality (double ql) {
this.ql = ql ;
}
void setLanguageQuality (HttpAcceptLanguage l) {
this.ql = l.getQuality() ;
}
double getLanguageQuality () {
return ql ;
}
ResourceReference getResource () {
return variant ;
}
double getQ() {
return qe * q * qs * ql ;
}
VariantState (ResourceReference variant, double qs) {
this.qs = qs ;
this.variant = variant ;
}
}
private static Class httpFrameClass = null;
static {
try {
httpFrameClass = Class.forName("org.w3c.jigsaw.frames.HTTPFrame") ;
} catch (Exception ex) {
throw new RuntimeException("No HTTPFrame class found.");
}
}
/**
* Our Icon property.
*/
public static String NEGOTIATED_ICON_P =
"org.w3c.jigsaw.frames.negotiated.icon";
/**
* Our default Icon
*/
public static String DEFAULT_NEGOTIATED_ICON = "generic.gif";
/**
* Turn debugging on/off.
*/
private static final boolean debug = false;
/**
* Minimum quality for a resource to be considered further.
*/
private static final double REQUIRED_QUALITY = 0.0001 ;
/**
* The Vary header field for this resource is always the same.
*/
protected static HttpTokenList VARY = null;
/**
* Attribute index - The set of names of variants.
*/
protected static int ATTR_VARIANTS = -1 ;
/**
* Attribute index - Should the PUT needs to be strictly checked?
*/
protected static int ATTR_PUT_POLICY = -1;
static {
// Compute and initialize the Vary header once and for all
String vary[] = { "Accept",
"Accept-Charset",
"Accept-Language",
"Accept-Encoding" };
VARY = HttpFactory.makeStringList(vary);
}
static {
Attribute a = null ;
Class cls = null ;
try {
cls = Class.forName("org.w3c.jigsaw.frames.NegotiatedFrame") ;
} catch (Exception ex) {
ex.printStackTrace() ;
System.exit(1) ;
}
// The names of the varint we negotiate
a = new StringArrayAttribute("variants"
, null
, Attribute.EDITABLE) ;
ATTR_VARIANTS = AttributeRegistry.registerAttribute(cls, a) ;
a = new BooleanAttribute("strict_put"
, new Boolean(true)
, Attribute.EDITABLE);
ATTR_PUT_POLICY = AttributeRegistry.registerAttribute(cls, a) ;
}
public String getIcon() {
String icon = super.getIcon();
if (icon == null) {
icon =
getServer().getProperties().getString(NEGOTIATED_ICON_P,
DEFAULT_NEGOTIATED_ICON);
setValue(ATTR_ICON, icon);
}
return icon;
}
/**
* Get the variant names.
*/
public String[] getVariantNames() {
return (String[]) getValue(ATTR_VARIANTS, null) ;
}
public void setVariants(String variants[]) {
setValue(ATTR_VARIANTS, variants);
}
/**
* get the "strictness" of the PUT checking
*/
public boolean getPutPolicy() {
Boolean val = (Boolean) getValue(ATTR_PUT_POLICY, null);
if (val == null) // strict by default
return true;
return val.booleanValue();
}
public void setPutPolicy(Boolean strict) {
setValue(ATTR_PUT_POLICY, strict);
}
public void setPutPolicy(boolean strict) {
setValue(ATTR_PUT_POLICY, new Boolean(strict));
}
/**
* Get the variant resources.
* This is somehow broken, it shouldn't allocate the array of variants
* on each call. However, don't forget that the list of variants can be
* dynamically edited, this means that if we are to keep a cache of it
* (as would be the case if we kept the array of variants as instance var)
* we should also take care of editing of attributes (possible, but I
* just don't have enough lifes).
* @return An array of ResourceReference, or <strong>null</strong>.
* @exception ProtocolException If one of the variants doesn't exist.
*/
public ResourceReference[] getVariantResources()
throws ProtocolException
{
// Get the variant names:
String names[] = getVariantNames() ;
if ( names == null )
return null ;
// Look them up in our parent directory:
ResourceReference variants[] = new ResourceReference[names.length] ;
ResourceReference r_parent = resource.getParent() ;
try {
DirectoryResource parent = (DirectoryResource) r_parent.lock();
int missing = 0;
for (int i = 0 ; i < names.length ; i++) {
variants[i] = parent.lookup(names[i]) ;
if (variants[i] == null)
missing++;
}
if (missing > 0) {
int kept = names.length - missing;
if (kept < 1)
return null;
String newNames[] = new String[kept];
int j = 0; int i = 0;
while (i < variants.length) {
if (variants[i] != null) {
newNames[j++] = names[i++];
} else {
i++;
}
}
setVariants(newNames);
//recompute Variant Resources
return getVariantResources();
}
} catch (InvalidResourceException ex) {
throw new HTTPException("invalid parent for negotiation");
} finally {
r_parent.unlock();
}
return variants ;
}
/**
* Print the current negotiation state.
* @param header The header to print first.
* @param states The current negotiation states.
*/
protected void printNegotiationState (String header, Vector states) {
if ( debug ) {
System.out.println ("------" + header) ;
for (int i = 0 ; i < states.size() ; i++) {
VariantState state = (VariantState) states.elementAt(i) ;
System.out.println (state) ;
}
System.out.println ("-----") ;
}
}
/**
* Negotiate among content encodings.
* <p>BUG: This will work only for single encoded variants.
* @param states The current negotiation states.
* @param request The request to handle.
* @return a boolean.
* @exception ProtocolException If one of the variants doesn't exist.
*/
protected boolean negotiateContentEncoding (Vector states,
Request request)
throws ProtocolException
{
if ( ! request.hasAcceptEncoding() ) {
// All encodings accepted:
for (int i = 0 ; i < states.size() ; i++) {
VariantState state = (VariantState) states.elementAt(i) ;
state.setContentEncodingQuality(1.0) ;
}
} else {
HttpAcceptEncoding encodings[] = request.getAcceptEncoding() ;
for (int i = 0 ; i < states.size() ; i++) {
VariantState state = (VariantState) states.elementAt(i) ;
ResourceReference rr = state.getResource();
try {
FramedResource resource = (FramedResource)rr.lock() ;
HTTPFrame itsframe =
(HTTPFrame) resource.getFrame(httpFrameClass);
if (itsframe != null) {
String ve;
if ( !itsframe.definesAttribute("content-encoding") ) {
ve = "identity"; // default encoding
state.setContentEncodingQuality (1.0) ;
} else {
ve = itsframe.getContentEncoding() ;
state.setContentEncodingQuality (0.001) ;
}
int jidx = -1 ;
for (int j = 0 ; j < encodings.length ; j++) {
if (encodings[j].getEncoding().equals(ve)) {
jidx = j;
break;
}
if (encodings[j].getEncoding().equals("*"))
jidx = j; // default '*' if no better match
}
if ( jidx >= 0 )
state.setContentEncodingQuality
(encodings[jidx]) ;
}
} catch (InvalidResourceException ex) {
} finally {
rr.unlock();
}
}
// FIXME We should check here against unlegible variants as now
}
return false ;
}
/**
* Negotiate on charsets.
* <p>BUG: Not implemented yet.
* @param states The current states of negotiation.
* @param request The request to handle.
*/
protected boolean negotiateCharsetQuality (Vector states
, Request request) {
return false ;
}
/**
* Negotiate among language qualities.
* <p>BUG: This will only work for variants that have one language tag.
* @param states The current states of negotiation.
* @param request The request to handle.
* @return a boolean.
* @exception ProtocolException If one of the variants doesn't exist.
*/
protected boolean negotiateLanguageQuality (Vector states
, Request request)
throws ProtocolException
{
if ( ! request.hasAcceptLanguage() ) {
for (int i = 0 ; i < states.size() ; i++) {
VariantState state = (VariantState) states.elementAt(i) ;
state.setLanguageQuality (1.0) ;
}
} else {
HttpAcceptLanguage languages[] = request.getAcceptLanguage() ;
boolean varyLang = false ;
for (int i = 0 ; i < states.size() ; i++) {
VariantState state = (VariantState) states.elementAt(i) ;
ResourceReference rr = state.getResource();
try {
FramedResource resource = (FramedResource)rr.lock() ;
HTTPFrame itsframe =
(HTTPFrame) resource.getFrame(httpFrameClass);
if (itsframe != null) {
if ( !itsframe.definesAttribute("content-language") ) {
state.setLanguageQuality (-1.0) ;
} else {
varyLang = true ;
String lang = itsframe.getContentLanguage() ;
int jidx = -1 ;
for (int j = 0 ; j < languages.length ; j++) {
if ( languages[j].getLanguage().equals(lang) )
jidx = j ;
}
if ( jidx < 0 )
state.setLanguageQuality(0.001) ;
else
state.setLanguageQuality (languages[jidx]) ;
}
}
} catch (InvalidResourceException ex) {
//FIXME
} finally {
rr.unlock();
}
}
if ( varyLang ) {
for (int i = 0 ; i < states.size() ; i++) {
VariantState s = (VariantState) states.elementAt(i);
if ( s.getLanguageQuality() < 0 )
s.setLanguageQuality (0.5) ;
}
} else {
for (int i = 0 ; i < states.size() ; i++) {
VariantState s = (VariantState) states.elementAt(i) ;
s.setLanguageQuality (1.0) ;
}
}
}
return false ;
}
/**
* Negotiate among content types.
* @param states The current states of negotiation.
* @param request The request to handle.
* @return a boolean.
* @exception ProtocolException If one of the variants doesn't exist.
*/
protected boolean negotiateContentType (Vector states,
Request request)
throws ProtocolException
{
if ( ! request.hasAccept() ) {
// All variants get a quality of 1.0
for (int i = 0 ; i < states.size() ; i++) {
VariantState state = (VariantState) states.elementAt(i) ;
state.setQuality (1.0) ;
}
} else {
// The browser has given some preferences:
HttpAccept accepts[] = request.getAccept() ;
// added by gisburn 16.4.1999:
// some browsers like Netscape 4.51 adding */* in their accept header
// which confuses this negotiation algorithm :-(
// the */* is replaced here (hack method, there should be a better way)
// with text/html - assuming that */ is (one of) the last entry(ies)
// in the accept line and that there is only ONE */*
// this fixed my problems with browsers "accepting" both text/xml and text/html
try
{
for( int z = 0 ; z < accepts.length ; z++ )
{
MimeType m = accepts[ z ].getMimeType();
// replace */* with text/html
if( m.toString().startsWith( "*/*" ) )
{
accepts[ z ].setMimeType( new MimeType( "text/html" ) );
break;
}
}
}
catch( Exception exc )
{
// bah, should not occur, but...
exc.printStackTrace();
}
for (int i = 0 ; i < states.size() ; i++ ) {
VariantState state = (VariantState) states.elementAt(i) ;
// Get the most specific match for this variant:
ResourceReference rr = state.getResource();
try {
FramedResource resource = (FramedResource)rr.lock() ;
HTTPFrame itsframe =
(HTTPFrame) resource.getFrame(httpFrameClass);
if (itsframe != null) {
MimeType vt = itsframe.getContentType();
int jmatch = -1 ;
int jidx = -1 ;
for (int j = 0 ; j < accepts.length ; j++) {
int match = vt.match (accepts[j].getMimeType()) ;
if ( match > jmatch ) {
jmatch = match ;
jidx = j ;
}
}
if ( jidx < 0 )
state.setQuality (0.0) ;
else
state.setQuality(accepts[jidx]) ;
}
} catch (InvalidResourceException ex) {
//FIXME
} finally {
rr.unlock();
}
}
}
return false ;
}
/**
* Negotiate among the various variants for the Resource.
* We made our best efforts to be as compliant as possible to the HTTP/1.0
* content negotiation algorithm.
* @param request the incomming request.
* @return a RefourceReference instance.
* @exception ProtocolException If one of the variants doesn't exist.
*/
protected ResourceReference negotiate (Request request)
throws ProtocolException
{
// Check for zero or one variant:
ResourceReference variants[] = getVariantResources() ;
if (variants == null) {
try {
getResource().delete();
} catch (MultipleLockException ex) {
//will be deleted later...
} finally {
Reply reply = request.makeReply(HTTP.NOT_FOUND);
reply.setContent ("<h1>Document not found</h1>"+
"<p>The document "+request.getURL()+
" has no acceptable variants "+
"(probably deleted).");
throw new HTTPException (reply);
}
}
if ( variants.length < 2 ) {
if ( variants.length == 0 ) {
try {
getResource().delete();
} catch (MultipleLockException ex) {
//will be deleted later...
} finally {
Reply reply = request.makeReply(HTTP.NOT_FOUND);
reply.setContent ("<h1>Document not found</h1>"+
"<p>The document "+request.getURL()+
" has no acceptable variants "+
"(probably deleted).");
throw new HTTPException (reply);
}
} else {
return variants[0] ;
}
}
// Build a vector of variant negociation states, one per variants:
Vector states = new Vector (variants.length) ;
for (int i = 0 ; i < variants.length ; i++) {
double qs = 1.0 ;
try {
FramedResource resource = (FramedResource)variants[i].lock() ;
HTTPFrame itsframe =
(HTTPFrame) resource.getFrame(httpFrameClass);
if (itsframe != null) {
if ( itsframe.definesAttribute ("quality") )
qs = itsframe.getQuality() ;
if ( qs > REQUIRED_QUALITY )
states.addElement(new VariantState (variants[i], qs)) ;
}
} catch (InvalidResourceException ex) {
//FIXME
} finally {
variants[i].unlock();
}
}
// Content-encoding negociation:
if ( debug )
printNegotiationState ("init:", states) ;
if ( negotiateContentEncoding (states, request) )
// Remains a single acceptable variant:
return ((VariantState) states.elementAt(0)).getResource() ;
if ( debug )
printNegotiationState ("encoding:", states) ;
// Charset quality negociation:
if ( negotiateCharsetQuality (states, request) )
// Remains a single acceptable variant:
return ((VariantState) states.elementAt(0)).getResource() ;
if ( debug )
printNegotiationState ("charset:", states) ;
// Language quality negociation:
if ( negotiateLanguageQuality (states, request) )
// Remains a single acceptable variant:
return ((VariantState) states.elementAt(0)).getResource() ;
if ( debug )
printNegotiationState ("language:", states) ;
// Content-type negociation:
if ( negotiateContentType (states, request) )
// Remains a single acceptable variant:
return ((VariantState) states.elementAt(0)).getResource() ;
if ( debug )
printNegotiationState ("type:", states) ;
// If we reached this point, this means that multiple variants are
// acceptable at this point. Keep the ones that have the best quality.
if ( debug )
printNegotiationState ("before Q selection:", states) ;
double qmax = REQUIRED_QUALITY ;
for (int i=0; i< states.size() ; ) {
VariantState state = (VariantState) states.elementAt(i) ;
if ( state.getQ() > qmax ) {
for (int j = i ; j > 0 ; j--)
states.removeElementAt(0) ;
qmax = state.getQ() ;
i = 1 ;
} else {
if ( state.getQ() < qmax)
states.removeElementAt(i) ;
else
i++;
}
}
if ( debug )
printNegotiationState ("After Q selection:", states) ;
if ( qmax == REQUIRED_QUALITY ) {
Reply reply = request.makeReply(HTTP.NOT_ACCEPTABLE) ;
HtmlGenerator g = new HtmlGenerator("No acceptable");
g.append("<P>The resource cannot be served according to the "
+ "headers sent</P>");
reply.setStream (g) ;
throw new HTTPException (reply) ;
} else if ( states.size() == 1 ) {
return ((VariantState) states.elementAt(0)).getResource() ;
} else {
// Respond with multiple choice (for the time being, there should
// be a parameter to decide what to do.
Reply reply = request.makeReply(HTTP.MULTIPLE_CHOICE) ;
HtmlGenerator g = new HtmlGenerator ("Multiple choice for "+
resource.getIdentifier()) ;
g.append ("<ul>") ;
for (int i = 0 ; i < states.size() ; i++) {
VariantState state = (VariantState) states.elementAt(i) ;
String name = null;
ResourceReference rr = state.getResource();
try {
name = rr.lock().getIdentifier();
g.append ("<li>"
+ "<a href=\"" + name + "\">" + name + "</a>"
+ " Q= " + state.getQ()) ;
} catch (InvalidResourceException ex) {
//FIXME
} finally {
rr.unlock();
}
}
reply.setStream (g) ;
reply.setHeaderValue(reply.H_VARY, VARY);
throw new HTTPException (reply) ;
}
}
/**
* "negotiate" for a PUT, the negotiation of a PUT should be
* different as we just want to match the desciption of the entity
* and the available variants
* @param request, the request to handle
* @return a ResourceReference instance
* @exception ProtocolException If negotiating among the resource variants
* failed.
* @exception ResourceException If the resource got a fatal error.
*/
protected ResourceReference negotiatePut(Request request)
throws ProtocolException, ResourceException
{
// Check for zero or one variant:
ResourceReference variants[] = getVariantResources() ;
HTTPFrame itsframe;
int nb_v;
// zero, don't PUT on a negotiable resource!
if (variants == null || variants.length == 0) {
try {
getResource().delete();
} catch (MultipleLockException ex) {
//will be deleted later...
} finally {
Reply reply = request.makeReply(HTTP.NOT_FOUND);
reply.setContent ("<h1>Document not found</h1>"+
"<p>The document "+request.getURL()+
" has no acceptable variants "+
"(probably deleted).");
throw new HTTPException (reply);
}
}
// negotiate etag
HttpEntityTag etag = request.getETag();
HttpEntityTag etags[] = request.getIfMatch();
// gather the etags
if (etags == null && etag != null) {
etags = new HttpEntityTag[1];
etags[0] = etag;
} else if (etag != null) {
HttpEntityTag t_etags[] = new HttpEntityTag[etags.length+1];
System.arraycopy(etags, 0, t_etags, 0, etags.length);
t_etags[etags.length] = etag;
etags = t_etags;
}
if (etags != null) {
// yeah go for it!
FramedResource resource;
HttpEntityTag frametag;
for (int i = 0 ; i < variants.length ; i++) {
try {
resource = (FramedResource)variants[i].lock() ;
itsframe = (HTTPFrame)resource.getFrame(httpFrameClass);
if (itsframe != null) {
frametag = itsframe.getETag();
// Do we have a winner?
for (int j=0; j<etags.length; j++)
if (frametag.getTag().equals(etags[j].getTag()))
return variants[i];
}
} catch (InvalidResourceException ex) {
//FIXME
} finally {
variants[i].unlock();
}
}
// no matching variants...
Reply reply = request.makeReply(HTTP.NOT_FOUND);
reply.setContent ("<h1>Document not found</h1>"+
"<p>The document "+request.getURL()+
" has no acceptable variants "+
"according to the ETag sent");
throw new HTTPException (reply);
}
// if we are strict, don't go any further, etags
// is the mandatory thing, otherwise PUT on the direct version
if (getPutPolicy()) {
Reply reply = request.makeReply(HTTP.NOT_FOUND);
reply.setContent ("<h1>Document not found</h1>"+
"<p>The document "+request.getURL()+
" has no acceptable variants "+
" for a PUT, as no ETags were sent");
throw new HTTPException (reply);
}
// now filter out variants
nb_v = variants.length;
MimeType type = request.getContentType();
String encodings[] = request.getContentEncoding();
String languages[] = request.getContentLanguage();
ResourceReference rr;
if (type != null || encodings != null || languages != null) {
// the request is not too bad ;)
for (int i = 0 ; i < variants.length ; i++) {
if (variants[i] == null)
continue;
rr = variants[i];
try {
resource = (FramedResource)rr.lock() ;
itsframe = (HTTPFrame)resource.getFrame(httpFrameClass);
if (itsframe == null) {
nb_v--;
variants[i] = null;
continue;
}
// remove the non matching mime types
if (type != null) {
MimeType fmt = itsframe.getContentType();
if (fmt == null || (fmt.match(type) !=
MimeType.MATCH_SPECIFIC_SUBTYPE)) {
nb_v--;
variants[i] = null;
continue;
}
}
// remove the non matching languages
if (languages != null) {
String language = itsframe.getContentLanguage();
nb_v--;
variants[i] = null;
if (language == null) {
continue;
}
for (int j=0; j<languages.length; j++) {
if (language.equals(languages[j])) {
nb_v++;
variants[i] = rr;
break;
}
}
}
// remove the non matching encodings
if (encodings != null) {
String encoding = itsframe.getContentEncoding();
nb_v--;
variants[i] = null;
if (encoding == null) {
continue;
}
for (int j=0; j<encodings.length; j++) {
if (encoding.equals(languages[j])) {
nb_v++;
variants[i] = rr;
break;
}
}
}
} catch (InvalidResourceException ex) {
//FIXME
} finally {
rr.unlock();
}
}
// a winner!
if (nb_v == 1) {
for (int i=0; i< variants.length; i++) {
if (variants[i] != null)
return variants[i];
}
}
// no document matching
if (nb_v <= 0 ) {
Reply reply = request.makeReply(HTTP.NOT_FOUND);
reply.setContent ("<h1>Document not found</h1>"+
"<p>The document "+request.getURL()+
" has no acceptable variants "+
" for a PUT");
throw new HTTPException (reply);
}
}
// now we have multiple choice :(
String name;
Reply reply = request.makeReply(HTTP.MULTIPLE_CHOICE) ;
HtmlGenerator g = new HtmlGenerator ("Multiple choice for "+
resource.getIdentifier()) ;
addStyleSheet( g );
g.append ("<ul>") ;
for (int i = 0 ; i < variants.length ; i++) {
if (variants[i] != null) {
try {
name = variants[i].lock().getIdentifier();
g.append ("<li>"
+ "<a href=\"" + name + "\">" +name+ "</a>");
} catch (InvalidResourceException ex) {
//FIXME (this should NOT happen :) )
} finally {
variants[i].unlock();
}
}
}
reply.setStream (g) ;
reply.setHeaderValue(reply.H_VARY, VARY);
throw new HTTPException (reply) ;
}
public void registerResource(FramedResource resource) {
super.registerOtherResource(resource);
}
/**
* Perform an HTTP request.
* Negotiate among the variants, the best variant according to the request
* fields, and make this elected variant serve the request.
* @param request The request to handle.
* @exception ProtocolException If negotiating among the resource variants
* failed.
* @exception ResourceException If the resource got a fatal error.
*/
public ReplyInterface perform(RequestInterface req)
throws ProtocolException, ResourceException
{
ReplyInterface repi = performFrames(req);
if (repi != null)
return repi;
if (! checkRequest(req))
return null;
Request request = (Request) req;
ResourceReference selected;
// Run content negotiation now:
// The PUT is special, we negotiate with ETag (if present) or
// using the metainformation about the PUT entity.
String method = request.getMethod ();
if (method.equals("PUT"))
selected = negotiatePut(request);
else
selected = negotiate(request);
// This should never happen: either the negotiation succeed, or the
// negotiate method should return an error.
if ( selected == null ) {
Reply error = request.makeReply(HTTP.INTERNAL_SERVER_ERROR) ;
error.setContent("Error negotiating among resource's variants.");
throw new HTTPException(error) ;
}
// FIXME content neg should be done at lookup time
// FIXME enhencing the reply should be done at outgoingfilter
// Get the original variant reply, and add its location as a header:
try {
FramedResource resource = (FramedResource) selected.lock();
Reply reply = (Reply)resource.perform(request) ;
reply.setHeaderValue(reply.H_VARY, VARY);
HTTPFrame itsframe =
(HTTPFrame) resource.getFrame(httpFrameClass);
if (itsframe != null) {
reply.setContentLocation(
itsframe.getURL(request).toExternalForm()) ;
return reply;
}
Reply error = request.makeReply(HTTP.INTERNAL_SERVER_ERROR) ;
error.setContent("Error negotiating : "+
"selected resource has no HTTPFrame");
throw new HTTPException(error) ;
} catch (InvalidResourceException ex) {
Reply error = request.makeReply(HTTP.INTERNAL_SERVER_ERROR) ;
error.setContent("Error negotiating : Invalid selected resource");
throw new HTTPException(error) ;
} finally {
selected.unlock();
}
}
}
Received on Friday, 16 April 1999 19:06:33 UTC