--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"\r
+ "http://www.w3.org/TR/REC-html40/strict.dtd">\r
+<html>\r
+ <head>\r
+ <title>NegotiatedFrame.java</title>\r
+ <meta name="Author" content="Benoit Mahe">\r
+ <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">\r
+ <meta name="Generator" content="*emacs: emacs-css">\r
+\r
+ <link rel="Stylesheet" media="screen" type="text/css" href="default-html.css">\r
+ </head>\r
+ <body>\r
+\r
+ <pre>\r
+<span class="comment">// NegotiatedFrame.java\r
+// $Id: NegotiatedFrame.html,v 1.1 2010/06/15 12:20:05 smhuang Exp $\r
+// (c) COPYRIGHT MIT and INRIA, 1996.\r
+// Please first read the full copyright statement in file COPYRIGHT.html\r
+</span>\r
+<span class="keyword">package</span> <span class="reference">org</span>.<span class="reference">w3c</span>.<span class="reference">jigsaw</span>.<span class="type">frames</span>;\r
+\r
+<span class="keyword">import</span> <span class="reference">java</span>.<span class="reference">io</span>.*;\r
+<span class="keyword">import</span> <span class="reference">java</span>.<span class="reference">util</span>.*;\r
+\r
+<span class="keyword">import</span> <span class="reference">org</span>.<span class="reference">w3c</span>.<span class="reference">tools</span>.<span class="reference">resources</span>.*;\r
+<span class="keyword">import</span> <span class="reference">org</span>.<span class="reference">w3c</span>.<span class="reference">jigsaw</span>.<span class="reference">http</span>.* ;\r
+<span class="keyword">import</span> <span class="reference">org</span>.<span class="reference">w3c</span>.<span class="reference">jigsaw</span>.<span class="reference">html</span>.*;\r
+<span class="keyword">import</span> <span class="reference">org</span>.<span class="reference">w3c</span>.<span class="reference">jigsaw</span>.<span class="reference">html</span>.<span class="type">HtmlGenerator</span> ;\r
+<span class="keyword">import</span> <span class="reference">org</span>.<span class="reference">w3c</span>.<span class="reference">www</span>.<span class="reference">mime</span>.* ;\r
+<span class="keyword">import</span> <span class="reference">org</span>.<span class="reference">w3c</span>.<span class="reference">www</span>.<span class="reference">http</span>.*;\r
+\r
+<span class="keyword">import</span> <span class="reference">org</span>.<span class="reference">w3c</span>.<span class="reference">tools</span>.<span class="reference">resources</span>.<span class="type">ProtocolException</span>;\r
+<span class="keyword">import</span> <span class="reference">org</span>.<span class="reference">w3c</span>.<span class="reference">tools</span>.<span class="reference">resources</span>.<span class="type">NotAProtocolException</span>;\r
+\r
+<span class="comment">/**\r
+ * Content negotiation.\r
+ */</span>\r
+<span class="reference">public</span> <span class="keyword">class</span> <span class="function-name">NegotiatedFrame</span> <span class="keyword">extends</span> <span class="type">HTTPFrame</span> {\r
+ \r
+ <span class="keyword">class</span> <span class="function-name">VariantState</span> {\r
+ <span class="type">ResourceReference</span> <span class="variable-name">variant</span> = <span class="keyword">null</span> ;\r
+ <span class="type">double</span> <span class="variable-name">qs</span> = 0.0 ;\r
+ <span class="type">double</span> <span class="variable-name">qe</span> = 0.0 ;\r
+ <span class="type">double</span> <span class="variable-name">qc</span> = 0.0 ;\r
+ <span class="type">double</span> <span class="variable-name">ql</span> = 0.0 ;\r
+ <span class="type">double</span> <span class="variable-name">q</span> = 0.0 ; <span class="comment">// quality (mime type one)\r
+</span> <span class="type">double</span> <span class="variable-name">Q</span> = 0.0 ; <span class="comment">// the big Q\r
+</span>\r
+ <span class="reference">public</span> <span class="type">String</span> <span class="function-name">toString</span>() {\r
+ <span class="keyword">try</span> {\r
+ <span class="type">Resource</span> <span class="variable-name">res</span> = variant.lock();\r
+ <span class="type">String</span> <span class="variable-name">name</span> = (<span class="type">String</span>) res.getIdentifier() ;\r
+ <span class="keyword">if</span> ( name == <span class="keyword">null</span> )\r
+ name = "<span class="string"><noname></span>" ;\r
+ <span class="keyword">return</span> "<span class="string">[</span>" + name \r
+ + "<span class="string"> qs=</span>" + qs \r
+ + "<span class="string"> qe=</span>" + qe\r
+ + "<span class="string"> ql=</span>" + ql\r
+ + "<span class="string"> q =</span>" + q\r
+ + "<span class="string"> Q =</span>" + getQ() \r
+ +"<span class="string">]</span>" ;\r
+ } <span class="keyword">catch</span> (<span class="type">InvalidResourceException</span> <span class="variable-name">ex</span>) {\r
+ <span class="keyword">return</span> "<span class="string">invalid</span>";\r
+ } <span class="keyword">finally</span> {\r
+ variant.unlock();\r
+ }\r
+ }\r
+\r
+ <span class="type">void</span> <span class="function-name">setContentEncodingQuality</span> (<span class="type">double</span> <span class="variable-name">qe</span>) {\r
+ <span class="reference">this</span>.qe = qe ;\r
+ }\r
+\r
+ <span class="type">void</span> <span class="function-name">setQuality</span> (<span class="type">double</span> <span class="variable-name">q</span>) {\r
+ <span class="reference">this</span>.q = q ;\r
+ }\r
+\r
+ <span class="type">void</span> <span class="function-name">setQuality</span> (<span class="type">HttpAccept</span> <span class="variable-name">a</span>) {\r
+ q = a.getQuality() ;\r
+ }\r
+\r
+ <span class="type">void</span> <span class="function-name">setLanguageQuality</span> (<span class="type">double</span> <span class="variable-name">ql</span>) {\r
+ <span class="reference">this</span>.ql = ql ;\r
+ }\r
+\r
+ <span class="type">void</span> <span class="function-name">setLanguageQuality</span> (<span class="type">HttpAcceptLanguage</span> <span class="variable-name">l</span>) {\r
+ <span class="reference">this</span>.ql = l.getQuality() ;\r
+ }\r
+\r
+ <span class="type">double</span> <span class="function-name">getLanguageQuality</span> () {\r
+ <span class="keyword">return</span> ql ;\r
+ }\r
+\r
+ <span class="type">ResourceReference</span> <span class="function-name">getResource</span> () {\r
+ <span class="keyword">return</span> variant ;\r
+ }\r
+\r
+ <span class="type">double</span> <span class="function-name">getQ</span>() {\r
+ <span class="keyword">return</span> qe * q * qs * ql ;\r
+ }\r
+\r
+ <span class="function-name">VariantState</span> (<span class="type">ResourceReference</span> <span class="variable-name">variant</span>, <span class="type">double</span> <span class="variable-name">qs</span>) {\r
+ <span class="reference">this</span>.qs = qs ;\r
+ <span class="reference">this</span>.variant = variant ;\r
+ }\r
+ }\r
+\r
+ <span class="string">private</span> <span class="type">static</span> <span class="type">Class</span> <span class="variable-name">httpFrameClass</span> = <span class="keyword">null</span>;\r
+\r
+ <span class="type">static</span> {\r
+ <span class="keyword">try</span> {\r
+ httpFrameClass = Class.forName("<span class="string">org.w3c.jigsaw.frames.HTTPFrame</span>") ;\r
+ } <span class="keyword">catch</span> (<span class="type">Exception</span> <span class="variable-name">ex</span>) {\r
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="type">RuntimeException</span>("<span class="string">No HTTPFrame class found.</span>");\r
+ }\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Turn debugging on/off.\r
+ */</span>\r
+ <span class="string">private</span> <span class="type">static</span> <span class="type">final</span> <span class="type">boolean</span> <span class="variable-name">debug</span> = <span class="keyword">false</span>;\r
+ <span class="comment">/**\r
+ * Minimum quality for a resource to be considered further.\r
+ */</span>\r
+ <span class="string">private</span> <span class="type">static</span> <span class="type">final</span> <span class="type">double</span> <span class="variable-name">REQUIRED_QUALITY</span> = 0.0001 ;\r
+ <span class="comment">/**\r
+ * The Vary header field for this resource is always the same.\r
+ */</span>\r
+ <span class="preprocessor">protected</span> <span class="type">static</span> <span class="type">HttpTokenList</span> <span class="variable-name">VARY</span> = <span class="keyword">null</span>;\r
+\r
+ <span class="comment">/**\r
+ * Attribute index - The set of names of variants.\r
+ */</span>\r
+ <span class="preprocessor">protected</span> <span class="type">static</span> <span class="type">int</span> <span class="variable-name">ATTR_VARIANTS</span> = -1 ;\r
+\r
+ <span class="type">static</span> {\r
+ <span class="comment">// Compute and initialize the Vary header once and for all\r
+</span> <span class="type">String</span> <span class="variable-name">vary</span>[] = { "<span class="string">Accept</span>",\r
+ "<span class="string">Accept-Charset</span>",\r
+ "<span class="string">Accept-Language</span>",\r
+ "<span class="string">Accept-Encoding</span>" };\r
+ VARY = HttpFactory.makeStringList(vary);\r
+ }\r
+\r
+ <span class="type">static</span> {\r
+ <span class="type">Attribute</span> <span class="variable-name">a</span> = <span class="keyword">null</span> ;\r
+ <span class="type">Class</span> <span class="variable-name">cls</span> = <span class="keyword">null</span> ;\r
+ <span class="keyword">try</span> {\r
+ cls = Class.forName("<span class="string">org.w3c.jigsaw.frames.NegotiatedFrame</span>") ;\r
+ } <span class="keyword">catch</span> (<span class="type">Exception</span> <span class="variable-name">ex</span>) {\r
+ ex.printStackTrace() ;\r
+ System.exit(1) ;\r
+ }\r
+ <span class="comment">// The names of the varint we negotiate\r
+</span> a = <span class="keyword">new</span> <span class="type">StringArrayAttribute</span>("<span class="string">variants</span>"\r
+ , <span class="keyword">null</span>\r
+ , Attribute.EDITABLE) ;\r
+ ATTR_VARIANTS = AttributeRegistry.registerAttribute(cls, a) ;\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Get the variant names.\r
+ */</span>\r
+ <span class="reference">public</span> <span class="type">String</span>[] <span class="function-name">getVariantNames</span>() {\r
+ <span class="keyword">return</span> (<span class="type">String</span>[]) getValue(ATTR_VARIANTS, <span class="keyword">null</span>) ;\r
+ }\r
+\r
+ <span class="reference">public</span> <span class="type">void</span> <span class="function-name">setVariants</span>(<span class="type">String</span> <span class="variable-name">variants</span>[]) {\r
+ setValue(ATTR_VARIANTS, variants);\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Get the variant resources.\r
+ * This is somehow broken, it shouldn't allocate the array of variants\r
+ * on each call. However, don't forget that the list of variants can be\r
+ * dynamically edited, this means that if we are to keep a cache of it \r
+ * (as would be the case if we kept the array of variants as instance var)\r
+ * we should also take care of editing of attributes (possible, but I\r
+ * just don't have enough lifes).\r
+ * </span><span class="keyword">@return </span><span class="comment">An array of ResourceReference, or <strong>null</strong>.\r
+ * </span><span class="keyword">@exception </span><span class="type">ProtocolException</span><span class="comment"> If one of the variants doesn't exist.\r
+ */</span>\r
+\r
+ <span class="reference">public</span> <span class="type">ResourceReference</span>[] <span class="function-name">getVariantResources</span>() \r
+ <span class="keyword">throws</span> <span class="type">ProtocolException</span>\r
+ {\r
+ <span class="comment">// Get the variant names:\r
+</span> <span class="type">String</span> <span class="variable-name">names</span>[] = getVariantNames() ;\r
+ <span class="keyword">if</span> ( names == <span class="keyword">null</span> )\r
+ <span class="keyword">return</span> <span class="keyword">null</span> ;\r
+ <span class="comment">// Look them up in our parent directory:\r
+</span> <span class="type">ResourceReference</span> <span class="variable-name">variants</span>[] = <span class="keyword">new</span> <span class="type">ResourceReference</span>[names.length] ;\r
+ <span class="type">ResourceReference</span> <span class="variable-name">r_parent</span> = resource.getParent() ;\r
+ <span class="keyword">try</span> {\r
+ <span class="type">DirectoryResource</span> <span class="variable-name">parent</span> = (<span class="type">DirectoryResource</span>) r_parent.lock();\r
+ <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">i</span> = 0 ; i < names.length ; i++) {\r
+ variants[i] = parent.lookup(names[i]) ;\r
+ <span class="keyword">if</span> (variants[i] == <span class="keyword">null</span>)\r
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="type">HTTPException</span>(names[i]+\r
+ "<span class="string">: couldn't be restored.</span>");\r
+ }\r
+ } <span class="keyword">catch</span> (<span class="type">InvalidResourceException</span> <span class="variable-name">ex</span>) {\r
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="type">HTTPException</span>("<span class="string">invalid parent for negotiation</span>");\r
+ } <span class="keyword">finally</span> {\r
+ r_parent.unlock();\r
+ }\r
+ <span class="keyword">return</span> variants ;\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Print the current negotiation state.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">header</span><span class="comment"> The header to print first.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">states</span><span class="comment"> The current negotiation states.\r
+ */</span>\r
+ \r
+ <span class="preprocessor">protected</span> <span class="type">void</span> <span class="function-name">printNegotiationState</span> (<span class="type">String</span> <span class="variable-name">header</span>, <span class="type">Vector</span> <span class="variable-name">states</span>) {\r
+ <span class="keyword">if</span> ( debug ) {\r
+ System.out.println ("<span class="string">------</span>" + header) ;\r
+ <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">i</span> = 0 ; i < states.size() ; i++) {\r
+ <span class="type">VariantState</span> <span class="variable-name">state</span> = (<span class="type">VariantState</span>) states.elementAt(i) ;\r
+ System.out.println (state) ;\r
+ }\r
+ System.out.println ("<span class="string">-----</span>") ;\r
+ }\r
+ }\r
+ \r
+ <span class="comment">/**\r
+ * Negotiate among content encodings.\r
+ * <p>BUG: This will work only for single encoded variants.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">states</span><span class="comment"> The current negotiation states.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">request</span><span class="comment"> The request to handle.\r
+ */</span>\r
+ \r
+ <span class="preprocessor">protected</span> <span class="type">boolean</span> <span class="function-name">negotiateContentEncoding</span> (<span class="type">Vector</span> <span class="variable-name">states</span>,\r
+ <span class="type">Request</span> <span class="variable-name">request</span>) \r
+ <span class="keyword">throws</span> <span class="type">ProtocolException</span>\r
+ {\r
+ <span class="keyword">if</span> ( ! request.hasAcceptEncoding() ) {\r
+ <span class="comment">// All encodings accepted:\r
+</span> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">i</span> = 0 ; i < states.size() ; i++) {\r
+ <span class="type">VariantState</span> <span class="variable-name">state</span> = (<span class="type">VariantState</span>) states.elementAt(i) ;\r
+ state.setContentEncodingQuality(1.0) ;\r
+ }\r
+ } <span class="keyword">else</span> {\r
+ <span class="type">String</span> <span class="variable-name">encodings</span>[] = request.getAcceptEncoding() ;\r
+ <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">i</span> = 0 ; i < states.size() ; i++) {\r
+ <span class="type">VariantState</span> <span class="variable-name">state</span> = (<span class="type">VariantState</span>) states.elementAt(i) ;\r
+ <span class="type">ResourceReference</span> <span class="variable-name">rr</span> = state.getResource();\r
+ <span class="keyword">try</span> {\r
+ <span class="type">FramedResource</span> <span class="variable-name">resource</span> = (<span class="type">FramedResource</span>)rr.lock() ;\r
+ <span class="type">HTTPFrame</span> <span class="variable-name">itsframe</span> = \r
+ (<span class="type">HTTPFrame</span>) resource.getFrame(httpFrameClass);\r
+ <span class="keyword">if</span> (itsframe != <span class="keyword">null</span>) {\r
+ <span class="keyword">if</span> ( !itsframe.definesAttribute("<span class="string">content-encoding</span>") ) {\r
+ state.setContentEncodingQuality (1.0) ;\r
+ } <span class="keyword">else</span> {\r
+ <span class="type">String</span> <span class="variable-name">ve</span> = itsframe.getContentEncoding() ;\r
+ state.setContentEncodingQuality (0.001) ;\r
+ <span class="reference">encoding_loop</span>:\r
+ <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">j</span> = 0 ; j < encodings.length ; j++) {\r
+ <span class="keyword">if</span> ( ve.equals (encodings[j]) ) {\r
+ state.setContentEncodingQuality(1.0) ;\r
+ <span class="keyword">break</span> <span class="reference">encoding_loop</span> ;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ } <span class="keyword">catch</span> (<span class="type">InvalidResourceException</span> <span class="variable-name">ex</span>) {\r
+ \r
+ } <span class="keyword">finally</span> {\r
+ rr.unlock();\r
+ }\r
+ }\r
+ <span class="comment">// FIXME We should check here against unlegible variants as now\r
+</span> }\r
+ <span class="keyword">return</span> <span class="keyword">false</span> ;\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Negotiate on charsets.\r
+ * <p>BUG: Not implemented yet.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">states</span><span class="comment"> The current states of negotiation.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">request</span><span class="comment"> The request to handle.\r
+ */</span>\r
+\r
+ <span class="preprocessor">protected</span> <span class="type">boolean</span> <span class="function-name">negotiateCharsetQuality</span> (<span class="type">Vector</span> <span class="variable-name">states</span>\r
+ , <span class="type">Request</span> <span class="variable-name">request</span>) {\r
+ <span class="keyword">return</span> <span class="keyword">false</span> ;\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Negotiate among language qualities.\r
+ * <p>BUG: This will only work for variants that have one language tag.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">states</span><span class="comment"> The current states of negotiation.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">request</span><span class="comment"> The request to handle.\r
+ */</span>\r
+\r
+ <span class="preprocessor">protected</span> <span class="type">boolean</span> <span class="function-name">negotiateLanguageQuality</span> (<span class="type">Vector</span> <span class="variable-name">states</span>\r
+ , <span class="type">Request</span> <span class="variable-name">request</span>) \r
+ <span class="keyword">throws</span> <span class="type">ProtocolException</span>\r
+ {\r
+ <span class="keyword">if</span> ( ! request.hasAcceptLanguage() ) {\r
+ <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">i</span> = 0 ; i < states.size() ; i++) {\r
+ <span class="type">VariantState</span> <span class="variable-name">state</span> = (<span class="type">VariantState</span>) states.elementAt(i) ;\r
+ state.setLanguageQuality (1.0) ;\r
+ }\r
+ } <span class="keyword">else</span> {\r
+ <span class="type">HttpAcceptLanguage</span> <span class="variable-name">languages</span>[] = request.getAcceptLanguage() ;\r
+ <span class="type">boolean</span> <span class="variable-name">varyLang</span> = <span class="keyword">false</span> ;\r
+ <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">i</span> = 0 ; i < states.size() ; i++) {\r
+ <span class="type">VariantState</span> <span class="variable-name">state</span> = (<span class="type">VariantState</span>) states.elementAt(i) ;\r
+ <span class="type">ResourceReference</span> <span class="variable-name">rr</span> = state.getResource();\r
+ <span class="keyword">try</span> {\r
+ <span class="type">FramedResource</span> <span class="variable-name">resource</span> = (<span class="type">FramedResource</span>)rr.lock() ;\r
+ <span class="type">HTTPFrame</span> <span class="variable-name">itsframe</span> = \r
+ (<span class="type">HTTPFrame</span>) resource.getFrame(httpFrameClass);\r
+ <span class="keyword">if</span> (itsframe != <span class="keyword">null</span>) {\r
+ <span class="keyword">if</span> ( !itsframe.definesAttribute("<span class="string">content-language</span>") ) {\r
+ state.setLanguageQuality (-1.0) ;\r
+ } <span class="keyword">else</span> {\r
+ varyLang = <span class="keyword">true</span> ;\r
+ <span class="type">String</span> <span class="variable-name">lang</span> = itsframe.getContentLanguage() ;\r
+ <span class="type">int</span> <span class="variable-name">jidx</span> = -1 ;\r
+ <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">j</span> = 0 ; j < languages.length ; j++) {\r
+ <span class="keyword">if</span> ( languages[j].getLanguage().equals(lang) )\r
+ jidx = j ;\r
+ }\r
+ <span class="keyword">if</span> ( jidx < 0 ) \r
+ state.setLanguageQuality(0.001) ;\r
+ <span class="keyword">else</span> \r
+ state.setLanguageQuality (languages[jidx]) ;\r
+ }\r
+ }\r
+ } <span class="keyword">catch</span> (<span class="type">InvalidResourceException</span> <span class="variable-name">ex</span>) {\r
+ <span class="comment">//FIXME\r
+</span> } <span class="keyword">finally</span> {\r
+ rr.unlock();\r
+ }\r
+ }\r
+ <span class="keyword">if</span> ( varyLang ) {\r
+ <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">i</span> = 0 ; i < states.size() ; i++) {\r
+ <span class="type">VariantState</span> <span class="variable-name">s</span> = (<span class="type">VariantState</span>) states.elementAt(i);\r
+ <span class="keyword">if</span> ( s.getLanguageQuality() < 0 )\r
+ s.setLanguageQuality (0.5) ;\r
+ }\r
+ } <span class="keyword">else</span> {\r
+ <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">i</span> = 0 ; i < states.size() ; i++) {\r
+ <span class="type">VariantState</span> <span class="variable-name">s</span> = (<span class="type">VariantState</span>) states.elementAt(i) ;\r
+ s.setLanguageQuality (1.0) ;\r
+ }\r
+ }\r
+ }\r
+ <span class="keyword">return</span> <span class="keyword">false</span> ;\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Negotiate among content types.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">states</span><span class="comment"> The current states of negotiation.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">request</span><span class="comment"> The request to handle.\r
+ */</span>\r
+\r
+ <span class="preprocessor">protected</span> <span class="type">boolean</span> <span class="function-name">negotiateContentType</span> (<span class="type">Vector</span> <span class="variable-name">states</span>,\r
+ <span class="type">Request</span> <span class="variable-name">request</span>) \r
+ <span class="keyword">throws</span> <span class="type">ProtocolException</span>\r
+ {\r
+ <span class="keyword">if</span> ( ! request.hasAccept() ) {\r
+ <span class="comment">// All variants get a quality of 1.0\r
+</span> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">i</span> = 0 ; i < states.size() ; i++) {\r
+ <span class="type">VariantState</span> <span class="variable-name">state</span> = (<span class="type">VariantState</span>) states.elementAt(i) ;\r
+ state.setQuality (1.0) ;\r
+ }\r
+ } <span class="keyword">else</span> {\r
+ <span class="comment">// The browser has given some preferences:\r
+</span> <span class="type">HttpAccept</span> <span class="variable-name">accepts</span>[] = request.getAccept() ;\r
+ <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">i</span> = 0 ; i < states.size() ; i++ ) {\r
+ <span class="type">VariantState</span> <span class="variable-name">state</span> = (<span class="type">VariantState</span>) states.elementAt(i) ;\r
+ <span class="comment">// Get the most specific match for this variant:\r
+</span> <span class="type">ResourceReference</span> <span class="variable-name">rr</span> = state.getResource();\r
+ <span class="keyword">try</span> {\r
+ <span class="type">FramedResource</span> <span class="variable-name">resource</span> = (<span class="type">FramedResource</span>)rr.lock() ;\r
+ <span class="type">HTTPFrame</span> <span class="variable-name">itsframe</span> = \r
+ (<span class="type">HTTPFrame</span>) resource.getFrame(httpFrameClass);\r
+ <span class="keyword">if</span> (itsframe != <span class="keyword">null</span>) {\r
+ <span class="type">MimeType</span> <span class="variable-name">vt</span> = itsframe.getContentType();\r
+ <span class="type">int</span> <span class="variable-name">jmatch</span> = -1 ;\r
+ <span class="type">int</span> <span class="variable-name">jidx</span> = -1 ;\r
+ <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">j</span> = 0 ; j < accepts.length ; j++) {\r
+ <span class="type">int</span> <span class="variable-name">match</span> = vt.match (accepts[j].getMimeType()) ;\r
+ <span class="keyword">if</span> ( match > jmatch ) {\r
+ jmatch = match ;\r
+ jidx = j ;\r
+ }\r
+ }\r
+ <span class="keyword">if</span> ( jidx < 0 )\r
+ state.setQuality (0.0) ;\r
+ <span class="keyword">else</span> \r
+ state.setQuality(accepts[jidx]) ;\r
+ }\r
+ } <span class="keyword">catch</span> (<span class="type">InvalidResourceException</span> <span class="variable-name">ex</span>) {\r
+ <span class="comment">//FIXME\r
+</span> } <span class="keyword">finally</span> {\r
+ rr.unlock();\r
+ }\r
+ }\r
+ }\r
+ <span class="keyword">return</span> <span class="keyword">false</span> ;\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Negotiate among the various variants for the Resource.\r
+ * We made our best efforts to be as compliant as possible to the HTTP/1.0\r
+ * content negotiation algorithm.\r
+ */</span>\r
+ <span class="preprocessor">protected</span> <span class="type">ResourceReference</span> <span class="function-name">negotiate</span> (<span class="type">Request</span> <span class="variable-name">request</span>) \r
+ <span class="keyword">throws</span> <span class="type">ProtocolException</span>\r
+ {\r
+ <span class="comment">// Check for zero or one variant:\r
+</span> <span class="type">ResourceReference</span> <span class="variable-name">variants</span>[] = getVariantResources() ;\r
+ <span class="keyword">if</span> ( variants.length < 2 ) {\r
+ <span class="keyword">if</span> ( variants.length == 0 ) {\r
+ <span class="type">Reply</span> <span class="variable-name">reply</span> = request.makeReply(HTTP.NOT_ACCEPTABLE) ;\r
+ reply.setContent ("<span class="string"><p>No acceptable variants.</span>") ;\r
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="type">HTTPException</span> (reply) ;\r
+ } <span class="keyword">else</span> {\r
+ <span class="keyword">return</span> variants[0] ;\r
+ }\r
+ }\r
+ <span class="comment">// Build a vector of variant negociation states, one per variants:\r
+</span> <span class="type">Vector</span> <span class="variable-name">states</span> = <span class="keyword">new</span> <span class="type">Vector</span> (variants.length) ;\r
+ <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">i</span> = 0 ; i < variants.length ; i++) {\r
+ <span class="type">double</span> <span class="variable-name">qs</span> = 1.0 ;\r
+ <span class="keyword">try</span> {\r
+ <span class="type">FramedResource</span> <span class="variable-name">resource</span> = (<span class="type">FramedResource</span>)variants[i].lock() ;\r
+ <span class="type">HTTPFrame</span> <span class="variable-name">itsframe</span> = \r
+ (<span class="type">HTTPFrame</span>) resource.getFrame(httpFrameClass);\r
+ <span class="keyword">if</span> (itsframe != <span class="keyword">null</span>) {\r
+ <span class="keyword">if</span> ( itsframe.definesAttribute ("<span class="string">quality</span>") )\r
+ qs = itsframe.getQuality() ;\r
+ <span class="keyword">if</span> ( qs > REQUIRED_QUALITY )\r
+ states.addElement(<span class="keyword">new</span> <span class="type">VariantState</span> (variants[i], qs)) ;\r
+ }\r
+ } <span class="keyword">catch</span> (<span class="type">InvalidResourceException</span> <span class="variable-name">ex</span>) {\r
+ <span class="comment">//FIXME\r
+</span> } <span class="keyword">finally</span> {\r
+ variants[i].unlock();\r
+ }\r
+ }\r
+ <span class="comment">// Content-encoding negociation:\r
+</span> <span class="keyword">if</span> ( debug )\r
+ printNegotiationState ("<span class="string">init:</span>", states) ;\r
+ <span class="keyword">if</span> ( negotiateContentEncoding (states, request) ) \r
+ <span class="comment">// Remains a single acceptable variant:\r
+</span> <span class="keyword">return</span> ((<span class="type">VariantState</span>) states.elementAt(0)).getResource() ;\r
+ <span class="keyword">if</span> ( debug )\r
+ printNegotiationState ("<span class="string">encoding:</span>", states) ;\r
+ <span class="comment">// Charset quality negociation:\r
+</span> <span class="keyword">if</span> ( negotiateCharsetQuality (states, request) ) \r
+ <span class="comment">// Remains a single acceptable variant:\r
+</span> <span class="keyword">return</span> ((<span class="type">VariantState</span>) states.elementAt(0)).getResource() ;\r
+ <span class="keyword">if</span> ( debug )\r
+ printNegotiationState ("<span class="string">charset:</span>", states) ;\r
+ <span class="comment">// Language quality negociation:\r
+</span> <span class="keyword">if</span> ( negotiateLanguageQuality (states, request) ) \r
+ <span class="comment">// Remains a single acceptable variant:\r
+</span> <span class="keyword">return</span> ((<span class="type">VariantState</span>) states.elementAt(0)).getResource() ;\r
+ <span class="keyword">if</span> ( debug )\r
+ printNegotiationState ("<span class="string">language:</span>", states) ;\r
+ <span class="comment">// Content-type negociation:\r
+</span> <span class="keyword">if</span> ( negotiateContentType (states, request) )\r
+ <span class="comment">// Remains a single acceptable variant:\r
+</span> <span class="keyword">return</span> ((<span class="type">VariantState</span>) states.elementAt(0)).getResource() ;\r
+ <span class="keyword">if</span> ( debug )\r
+ printNegotiationState ("<span class="string">type:</span>", states) ;\r
+ <span class="comment">// If we reached this point, this means that multiple variants are \r
+</span> <span class="comment">// acceptable at this point. Keep the one that have the best quality.\r
+</span> <span class="keyword">if</span> ( debug )\r
+ printNegotiationState ("<span class="string">before Q selection:</span>", states) ;\r
+ <span class="type">double</span> <span class="variable-name">qmax</span> = REQUIRED_QUALITY ;\r
+ <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">i</span> = 0 ; i < states.size() ; ) {\r
+ <span class="type">VariantState</span> <span class="variable-name">state</span> = (<span class="type">VariantState</span>) states.elementAt(i) ;\r
+ <span class="keyword">if</span> ( state.getQ() > qmax ) {\r
+ <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">j</span> = i ; j > 0 ; j--)\r
+ states.removeElementAt(0) ;\r
+ qmax = state.getQ() ;\r
+ i = 1 ;\r
+ } <span class="keyword">else</span> {\r
+ states.removeElementAt(i) ;\r
+ }\r
+ }\r
+ <span class="keyword">if</span> ( debug )\r
+ printNegotiationState ("<span class="string">After Q selection:</span>", states) ;\r
+ <span class="keyword">if</span> ( qmax == REQUIRED_QUALITY ) {\r
+ <span class="type">Reply</span> <span class="variable-name">reply</span> = request.makeReply(HTTP.NOT_ACCEPTABLE) ;\r
+ reply.setContent ("<span class="string"><p>No acceptable variant.</span>") ;\r
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="type">HTTPException</span> (reply) ;\r
+ } <span class="keyword">else</span> <span class="keyword">if</span> ( states.size() == 1 ) {\r
+ <span class="keyword">return</span> ((<span class="type">VariantState</span>) states.elementAt(0)).getResource() ;\r
+ } <span class="keyword">else</span> {\r
+ <span class="comment">// Respond with multiple choice (for the time being, there should\r
+</span> <span class="comment">// be a parameter to decide what to do.\r
+</span> <span class="type">Reply</span> <span class="variable-name">reply</span> = request.makeReply(HTTP.MULTIPLE_CHOICE) ;\r
+ <span class="type">HtmlGenerator</span> <span class="variable-name">g</span> = <span class="keyword">new</span> <span class="type">HtmlGenerator</span> ("<span class="string">Multiple choice for </span>"+\r
+ resource.getIdentifier()) ;\r
+ g.append ("<span class="string"><ul></span>") ;\r
+ <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">i</span> = 0 ; i < states.size() ; i++) {\r
+ <span class="type">VariantState</span> <span class="variable-name">state</span> = (<span class="type">VariantState</span>) states.elementAt(i) ;\r
+ <span class="type">String</span> <span class="variable-name">name</span> = <span class="keyword">null</span>;\r
+ <span class="type">ResourceReference</span> <span class="variable-name">rr</span> = state.getResource();\r
+ <span class="keyword">try</span> {\r
+ name = rr.lock().getIdentifier();\r
+ g.append ("<span class="string"><li></span>" \r
+ + "<span class="string"><a href=\"</span>" + name + "<span class="string">\"></span>" + name + "<span class="string"></a></span>"\r
+ + "<span class="string"> Q= </span>" + state.getQ()) ;\r
+ } <span class="keyword">catch</span> (<span class="type">InvalidResourceException</span> <span class="variable-name">ex</span>) {\r
+ <span class="comment">//FIXME\r
+</span> } <span class="keyword">finally</span> {\r
+ rr.unlock();\r
+ }\r
+ }\r
+ reply.setStream (g) ;\r
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="type">HTTPException</span> (reply) ;\r
+ }\r
+ }\r
+\r
+ <span class="reference">public</span> <span class="type">void</span> <span class="function-name">registerResource</span>(<span class="type">FramedResource</span> <span class="variable-name">resource</span>) {\r
+ <span class="reference">super</span>.registerOtherResource(resource);\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Perform an HTTP request.\r
+ * Negotiate among the variants, the best variant according to the request\r
+ * fields, and make this elect3d variant serve the request.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">request</span><span class="comment"> The request to handle.\r
+ * </span><span class="keyword">@exception </span><span class="type">ProtocolException</span><span class="comment"> If negotiating among the resource variants \r
+ * failed.\r
+ */</span>\r
+\r
+ <span class="reference">public</span> <span class="type">ReplyInterface</span> <span class="function-name">perform</span>(<span class="type">RequestInterface</span> <span class="variable-name">req</span>)\r
+ <span class="keyword">throws</span> <span class="type">ProtocolException</span>, <span class="type">NotAProtocolException</span>\r
+ {\r
+ <span class="type">ReplyInterface</span> <span class="variable-name">repi</span> = performFrames(req);\r
+ <span class="keyword">if</span> (repi != <span class="keyword">null</span>)\r
+ <span class="keyword">return</span> repi;\r
+\r
+ <span class="keyword">if</span> (! checkRequest(req))\r
+ <span class="keyword">return</span> <span class="keyword">null</span>;\r
+\r
+ <span class="type">Request</span> <span class="variable-name">request</span> = (<span class="type">Request</span>) req;\r
+ <span class="comment">// Run content negotiation now:\r
+</span> <span class="type">ResourceReference</span> <span class="variable-name">selected</span> = negotiate(request) ;\r
+ <span class="comment">// This should never happen: either the negotiation succeed, or the\r
+</span> <span class="comment">// negotiate method should return an error.\r
+</span> <span class="keyword">if</span> ( selected == <span class="keyword">null</span> ) {\r
+ <span class="type">Reply</span> <span class="variable-name">error</span> = request.makeReply(HTTP.INTERNAL_SERVER_ERROR) ;\r
+ error.setContent("<span class="string">Error negotiating among resource's variants.</span>");\r
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="type">HTTPException</span>(error) ;\r
+ }\r
+ <span class="comment">// FIXME content neg should be done at lookup time\r
+</span> <span class="comment">// FIXME enhencing the reply should be done at outgoingfilter\r
+</span> <span class="comment">// Get the original variant reply, and add its location as a header:\r
+</span> <span class="keyword">try</span> {\r
+ <span class="type">FramedResource</span> <span class="variable-name">resource</span> = (<span class="type">FramedResource</span>) selected.lock();\r
+ <span class="type">Reply</span> <span class="variable-name">reply</span> = (<span class="type">Reply</span>)resource.perform(request) ;\r
+ reply.setHeaderValue(reply.H_VARY, VARY);\r
+ <span class="type">HTTPFrame</span> <span class="variable-name">itsframe</span> = \r
+ (<span class="type">HTTPFrame</span>) resource.getFrame(httpFrameClass);\r
+ <span class="keyword">if</span> (itsframe != <span class="keyword">null</span>) {\r
+ reply.setContentLocation(\r
+ itsframe.getURL(request).toExternalForm()) ;\r
+ <span class="keyword">return</span> reply;\r
+ }\r
+ <span class="type">Reply</span> <span class="variable-name">error</span> = request.makeReply(HTTP.INTERNAL_SERVER_ERROR) ;\r
+ error.setContent("<span class="string">Error negotiating : </span>"+\r
+ "<span class="string">selected resource has no HTTPFrame</span>");\r
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="type">HTTPException</span>(error) ;\r
+ } <span class="keyword">catch</span> (<span class="type">InvalidResourceException</span> <span class="variable-name">ex</span>) {\r
+ <span class="type">Reply</span> <span class="variable-name">error</span> = request.makeReply(HTTP.INTERNAL_SERVER_ERROR) ;\r
+ error.setContent("<span class="string">Error negotiating : Invalid selected resource</span>");\r
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="type">HTTPException</span>(error) ;\r
+ } <span class="keyword">finally</span> {\r
+ selected.unlock();\r
+ }\r
+ }\r
+ \r
+}\r
+\r
+ </pre>\r
+ </body>\r
+</html>\r