Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / jigsaw / src / org / w3c / jigsaw / frames / JpegComFrame.java
diff --git a/JMCR-Stable/real-world application/jigsaw/src/org/w3c/jigsaw/frames/JpegComFrame.java b/JMCR-Stable/real-world application/jigsaw/src/org/w3c/jigsaw/frames/JpegComFrame.java
new file mode 100644 (file)
index 0000000..cee533a
--- /dev/null
@@ -0,0 +1,632 @@
+// JpegComFrame.java\r
+// $Id: JpegComFrame.java,v 1.2 2010/06/15 17:52:53 smhuang Exp $\r
+// (c) COPYRIGHT MIT, INRIA and Keio, 1999.\r
+// Please first read the full copyright statement in file COPYRIGHT.html\r
+\r
+package org.w3c.jigsaw.frames;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.net.URLEncoder;\r
+import org.w3c.www.mime.MimeType;\r
+import org.w3c.www.mime.MimeTypeFormatException;\r
+import org.w3c.tools.resources.Attribute;\r
+import org.w3c.tools.resources.AttributeRegistry;\r
+import org.w3c.tools.resources.ProtocolException;\r
+import org.w3c.tools.resources.ResourceException;\r
+import org.w3c.tools.resources.event.AttributeChangedEvent;\r
+import org.w3c.www.http.HTTP;\r
+import org.w3c.www.http.HttpAccept;\r
+import org.w3c.www.http.HttpEntityTag;\r
+import org.w3c.www.http.HttpFactory;\r
+import org.w3c.www.http.HttpInvalidValueException;\r
+import org.w3c.jigsaw.http.Client;\r
+import org.w3c.jigsaw.http.ClientException;\r
+import org.w3c.jigsaw.http.HTTPException;\r
+import org.w3c.jigsaw.http.Reply;\r
+import org.w3c.jigsaw.http.Request;\r
+\r
+import org.w3c.tools.jpeg.JpegHeaders;\r
+import org.w3c.jigsaw.resources.ImageFileResource;\r
+\r
+/**\r
+ * This class will read the comments from a jpeg file and return it\r
+ * depending on the Accept: header\r
+ */\r
+\r
+public class JpegComFrame extends HTTPFrame {\r
+    public static final boolean debug = false;\r
+    /**\r
+     * Attribute index - The comment content type\r
+     */\r
+    protected static int ATTR_COM_TYPE = -1 ;\r
+\r
+    static {\r
+       Attribute   a = null ;\r
+       Class     cls = null ;\r
+       try {\r
+           cls = Class.forName("org.w3c.jigsaw.frames.JpegComFrame") ;\r
+           //Added by Jeff Huang\r
+           //TODO: FIXIT\r
+       } catch (Exception ex) {\r
+           ex.printStackTrace() ;\r
+           System.exit(1) ;\r
+       }\r
+       // The comment content type\r
+       a = new MimeTypeAttribute("comment-type",\r
+                                 null,\r
+                                 Attribute.EDITABLE) ;\r
+       ATTR_COM_TYPE = AttributeRegistry.registerAttribute(cls, a) ;\r
+    }\r
+\r
+    /**\r
+     * the static String of the Vary ehader to be added\r
+     */\r
+    protected static String[] vary = { "Accept" };\r
+\r
+    /**\r
+     * get the content type of the comment embedded in the picture\r
+     * @return a MimeType, or null if undefined\r
+     */\r
+    public MimeType getCommentType() {\r
+       return (MimeType)getValue(ATTR_COM_TYPE, null);\r
+    }\r
+\r
+    /**\r
+     * The comment entity tag\r
+     */\r
+    protected HttpEntityTag cometag = null;\r
+\r
+    /**\r
+     * The comment.\r
+     */\r
+    protected String comment = null;\r
+\r
+    /**\r
+     * Extract the comment from the jpeg image.\r
+     * @return the comment\r
+     */\r
+    protected String getMetadata() {\r
+       if (fresource == null)\r
+           return null;\r
+       File file = fresource.getFile();\r
+       if (file.exists()) {\r
+           String comments[] = null;\r
+           try {\r
+               JpegHeaders headers = new JpegHeaders(file);\r
+               comments = headers.getComments();\r
+           } catch (Exception ex) {\r
+               ex.printStackTrace();\r
+               return "unable to get comment: "+ex.getMessage();\r
+           }\r
+           comment = "";\r
+           for (int i = 0 ; i < comments.length ; i++)\r
+               comment += comments[i];\r
+           if (comment.equals(""))\r
+               comment = "no comment";\r
+       }\r
+       return comment;\r
+    }\r
+\r
+    /**\r
+     * Get the comment Etag\r
+     * @return an instance of HttpEntityTag, or <strong>null</strong> if not\r
+     *    defined.\r
+     */\r
+\r
+    public HttpEntityTag getComETag() {\r
+       if (cometag == null) {\r
+           String etag_s = null;\r
+           if (fresource != null) {\r
+               long lstamp = fresource.getFileStamp()+1;\r
+               if ( lstamp >= 0L ) {\r
+                   String soid  = Integer.toString(getOid(), 32);\r
+                   String stamp = Long.toString(lstamp, 32);\r
+                   etag_s = Integer.toString(getOid(), 32)+":"\r
+                       + Long.toString(lstamp, 32);\r
+               }\r
+           }\r
+           cometag = HttpFactory.makeETag(false, etag_s);\r
+       }\r
+       return cometag;\r
+    }\r
+\r
+    /**\r
+     * Update the cached headers value.\r
+     * Each resource maintains a set of cached values for headers, this\r
+     * allows for a nice sped-up in headers marshalling, which - as the \r
+     * complexity of the protocol increases - becomes a bottleneck.\r
+     */\r
+\r
+    protected void updateCachedHeaders() {\r
+       super.updateCachedHeaders();\r
+       if (comment == null) {\r
+           comment = getMetadata();\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Listen its resource.\r
+     */\r
+    public void attributeChanged(AttributeChangedEvent evt) {\r
+       super.attributeChanged(evt);\r
+       String name = evt.getAttribute().getName();\r
+       if ((name.equals("file-stamp")) || (name.equals("file-stamp")))\r
+           comment = null;\r
+    }\r
+\r
+    public Reply createCommentReply(Request request, int status) {\r
+       Reply reply = request.makeReply(status);\r
+       updateCachedHeaders();\r
+       reply.setContent(comment);\r
+       reply.setContentType(getCommentType());\r
+       reply.setVary(vary);\r
+       if ( lastmodified != null )\r
+           reply.setHeaderValue(Reply.H_LAST_MODIFIED, lastmodified);\r
+       if ( contentencoding != null )\r
+           reply.setHeaderValue(Reply.H_CONTENT_ENCODING,contentencoding);\r
+       if ( contentlanguage != null )\r
+           reply.setHeaderValue(Reply.H_CONTENT_LANGUAGE,contentlanguage);\r
+       long maxage = getMaxAge();\r
+       if ( maxage >= 0 ) {\r
+           if (reply.getMajorVersion() >= 1 ) {\r
+               if (reply.getMinorVersion() >= 1) {\r
+                   reply.setMaxAge((int) (maxage / 1000));\r
+               } \r
+               // If max-age is zero, say what you mean:\r
+               long expires = (System.currentTimeMillis()\r
+                               + ((maxage == 0) ? -1000 : maxage));\r
+               reply.setExpires(expires);\r
+           }\r
+       }\r
+       // Set the date of the reply (round it to secs):\r
+       reply.setDate((System.currentTimeMillis() / 1000L) * 1000L);\r
+       reply.setETag(getComETag());\r
+       String commenttype = getCommentType().toString();\r
+       reply.setContentLocation(getURL(request).toExternalForm() \r
+                                + ";" + URLEncoder.encode(commenttype));\r
+       return reply;\r
+    }\r
+\r
+    public Reply createCommentReply(Request request) {\r
+       return createCommentReply(request, HTTP.OK);\r
+    }\r
+\r
+    /**\r
+     * Check the <code>If-Match</code> condition of that request.\r
+     * @param request The request to check.\r
+     * @return An integer, either <code>COND_FAILED</cond> if condition\r
+     * was checked, but failed, <code>COND_OK</code> if condition was checked\r
+     * and succeeded, or <strong>0</strong> if the condition was not checked\r
+     * at all (eg because the resource or the request didn't support it).\r
+     */\r
+\r
+    public int checkIfMatch(Request request, HttpEntityTag etag) {\r
+       if (fresource != null) {\r
+           HttpEntityTag tags[] = request.getIfMatch();\r
+           if ( tags != null ) {\r
+               // Good, real validators in use:\r
+               if ( etag != null ) {\r
+                   // Note: if etag is null this means that the resource has \r
+                   // changed and has not been even emited since then...\r
+                   for (int i = 0 ; i < tags.length ; i++) {\r
+                       HttpEntityTag t = tags[i];\r
+                       if (t.getTag().equals(etag.getTag())) {\r
+                           if (t.isWeak() || etag.isWeak()) {\r
+                               return COND_WEAK;\r
+                           } else {\r
+                               return COND_OK;\r
+                           }\r
+                       }\r
+                   }\r
+               }\r
+               return COND_FAILED;\r
+           }\r
+       }\r
+       return 0;\r
+    }\r
+\r
+    /**\r
+     * Check the <code>If-None-Match</code> condition of that request.\r
+     * @param request The request to check.\r
+     * @return An integer, either <code>COND_FAILED</cond> if condition\r
+     * was checked, but failed, <code>COND_OK</code> if condition was checked\r
+     * and succeeded, or <strong>0</strong> if the condition was not checked\r
+     * at all (eg because the resource or the request didn't support it).\r
+     */\r
+\r
+    public int checkIfNoneMatch(Request request, HttpEntityTag etag) {\r
+       if (fresource != null) {\r
+           // Check for an If-None-Match conditional:\r
+           HttpEntityTag tags[] = request.getIfNoneMatch();\r
+           if ( tags != null ) {\r
+               if ( etag == null ) {\r
+                   return COND_OK;\r
+               }\r
+               int status = COND_OK;\r
+               for (int i = 0 ; i < tags.length ; i++) {\r
+                   HttpEntityTag t = tags[i];\r
+                   if (t.getTag().equals(etag.getTag())) {\r
+                       if (t.isWeak() || etag.isWeak()) {\r
+                           status = COND_WEAK;\r
+                       } else {\r
+                           return COND_FAILED;\r
+                       }\r
+                   }\r
+                   if (t.getTag().equals("*")) {\r
+                       if (fresource != null) {\r
+                           File f = fresource.getFile();\r
+                           if (f.exists()) {\r
+                               return COND_FAILED;\r
+                           }\r
+                       } else {\r
+                           return COND_FAILED;\r
+                       }\r
+                   }\r
+               }\r
+               return status;\r
+           }\r
+       }\r
+       return 0;\r
+    }\r
+\r
+    /**\r
+     * check the validators namely LMT/Etags according to rfc2616 rules\r
+     * @return An integer, either <code>COND_FAILED</cond> if condition\r
+     * was checked, but failed, <code>COND_OK</code> if condition was checked\r
+     * and succeeded, or <strong>0</strong> if the condition was not checked\r
+     * at all (eg because the resource or the request didn't support it).\r
+     */\r
+    public int checkValidators(Request request, HttpEntityTag etag) {\r
+       int v_inm = checkIfNoneMatch(request, etag);\r
+       int v_ims = checkIfModifiedSince(request);\r
+\r
+       if ((v_inm == COND_OK) || (v_ims == COND_OK)) {\r
+           return COND_OK;\r
+       }\r
+       if ((v_inm == COND_FAILED) || (v_ims == COND_FAILED)) {\r
+           return COND_FAILED;\r
+       }\r
+       if ((v_inm == COND_WEAK) || (v_ims == COND_WEAK)) {\r
+           return COND_OK;\r
+       }\r
+       return 0;\r
+    }\r
+\r
+    /**\r
+     * Negotiate.\r
+     * @param request the incomming request.\r
+     * @return true if the client wants the comment, false if the client \r
+     * wants the image.\r
+     */\r
+    protected boolean negotiate(Request request)\r
+       throws ProtocolException\r
+    {\r
+       if ( ! request.hasAccept() ) {\r
+           //return the image\r
+           return false;\r
+       } else {\r
+           // The browser has given some preferences:\r
+           HttpAccept accepts[] = request.getAccept() ;\r
+           \r
+           //two content types image/jpeg and comment-type\r
+           HttpAccept imgAccept = \r
+               getMatchingAccept(accepts, getContentType());\r
+           HttpAccept comAccept = \r
+               getMatchingAccept(accepts, getCommentType());\r
+           \r
+           if ((imgAccept != null) &&  (comAccept != null)) {\r
+               // go for best MIME match first\r
+               int matchImg = getContentType().match(imgAccept.getMimeType());\r
+               int matchCom = getCommentType().match(comAccept.getMimeType());\r
+\r
+               if (matchImg == matchCom) {\r
+                   // equals, use quality\r
+                   return (imgAccept.getQuality() < comAccept.getQuality());\r
+               } else {\r
+                   return (matchImg < matchCom);\r
+               }\r
+           } else if (comAccept != null)\r
+               return true;\r
+           else\r
+               return false;\r
+       }\r
+    }\r
+\r
+    protected HttpAccept getMatchingAccept(HttpAccept accepts[],\r
+                                          MimeType mime) \r
+    {\r
+       int jmatch = -1 ;\r
+       int jidx   = -1 ;\r
+       for (int i = 0 ; i < accepts.length ; i++) {\r
+           try {\r
+               int match = mime.match(accepts[i].getMimeType());\r
+               if ( match > jmatch ) {\r
+                   jmatch = match ;\r
+                   jidx   = i ;\r
+               }\r
+           } catch (HttpInvalidValueException ivex) {\r
+               // There is a bad acept header here\r
+               // let's be cool and ignore it\r
+               // FIXME we should answer with a Bad Request\r
+           }\r
+       }\r
+       if (jidx < 0)\r
+           return null;\r
+       return accepts[jidx];\r
+    }\r
+\r
+    /**\r
+     * Perform a HEAD request for the associated FileResource.\r
+     * @param request the incomming request.\r
+     * @return A Reply instance\r
+     * @exception ProtocolException If processsing the request failed.\r
+     * @exception ResourceException If the resource got a fatal error.\r
+     */\r
+    protected Reply headFileResource(Request request) \r
+       throws ProtocolException, ResourceException\r
+    {\r
+       if (fresource == null) \r
+           throw new ResourceException("this frame is not attached to a "+\r
+                                           "FileResource. ("+\r
+                                           resource.getIdentifier()+")");\r
+       Reply reply = null;\r
+       fresource.checkContent();\r
+       updateCachedHeaders();\r
+       // hack, if ;text/html is there,\r
+       // it will be added at first place of the accept \r
+       String param = null;\r
+       String sfile = request.getURL().getFile();\r
+       int pos = sfile.indexOf(';');\r
+       if (pos != -1) {\r
+           param = (String) request.getState("type");\r
+       }\r
+       if (param != null) {\r
+           HttpAccept acc[] = request.getAccept();\r
+           HttpAccept newacc[] = null;\r
+           if (acc != null) {\r
+               newacc = new HttpAccept[acc.length+1];\r
+               System.arraycopy(acc, 0, newacc, 1, acc.length);\r
+           } else {\r
+               newacc = new HttpAccept[1];\r
+           }\r
+           try {\r
+               newacc[0] = HttpFactory.makeAccept(new MimeType(param), 1.1);\r
+               request.setAccept(newacc);\r
+           } catch (MimeTypeFormatException ex) {\r
+               // not a valid mime type... maybe something else, do not care\r
+           }\r
+       }\r
+       boolean commentOnly = negotiate(request);\r
+       HttpEntityTag etag = null;\r
+       if (commentOnly)\r
+           etag = getComETag();\r
+       else\r
+           etag = getETag();\r
+       // Check validators:\r
+       int cim = checkIfMatch(request, etag);\r
+       if ((cim == COND_FAILED) || (cim == COND_WEAK)) {\r
+           reply = request.makeReply(HTTP.PRECONDITION_FAILED);\r
+           reply.setContent("Pre-conditions failed.");\r
+           reply.setContentMD5(null);\r
+           return reply;\r
+       }\r
+       if ( checkIfUnmodifiedSince(request) == COND_FAILED ) {\r
+           reply = request.makeReply(HTTP.PRECONDITION_FAILED);\r
+           reply.setContent("Pre-conditions failed.");\r
+           reply.setContentMD5(null);\r
+           return reply;\r
+       }\r
+       if (checkValidators(request, etag) == COND_FAILED) {\r
+           reply = createDefaultReply(request, HTTP.NOT_MODIFIED);\r
+           reply.setETag(etag);\r
+           reply.setContentMD5(null);\r
+           return reply;\r
+       }       \r
+       if (! fresource.getFile().exists()) {\r
+           return deleteMe(request);\r
+       } else {\r
+           if (commentOnly) {\r
+               reply = createCommentReply(request);\r
+               reply.setStream((InputStream) null);\r
+           } else {\r
+               reply = createDefaultReply(request, HTTP.OK);\r
+               reply.setVary(vary);\r
+           }\r
+           if (request.hasState(STATE_CONTENT_LOCATION))\r
+               reply.setContentLocation(getURL(request).toExternalForm());\r
+           return reply;\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Get for FileResource\r
+     * @param request the incomming request.\r
+     * @return A Reply instance\r
+     * @exception ProtocolException If processsing the request failed.\r
+     * @exception ResourceException If the resource got a fatal error.\r
+     */\r
+    protected Reply getFileResource(Request request) \r
+       throws ProtocolException, ResourceException\r
+    {\r
+       if (fresource == null) \r
+           throw new ResourceException("this frame is not attached to a "+\r
+                                       "FileResource. ("+\r
+                                       resource.getIdentifier()+")");\r
+       Reply reply = null;\r
+       File file = fresource.getFile() ;\r
+       fresource.checkContent();\r
+       updateCachedHeaders();\r
+       String param = null;\r
+       String sfile = request.getURL().getFile();\r
+       int pos = sfile.indexOf(';');\r
+       if (pos != -1) {\r
+           param = (String) request.getState("type");\r
+       }\r
+       if (param != null) {\r
+           HttpAccept acc[] = request.getAccept();\r
+           HttpAccept newacc[] = null;\r
+           if (acc != null) {\r
+               newacc = new HttpAccept[acc.length+1];\r
+               System.arraycopy(acc, 0, newacc, 1, acc.length);\r
+           } else {\r
+               newacc = new HttpAccept[1];\r
+           }\r
+           try {\r
+               newacc[0] = HttpFactory.makeAccept(new MimeType(param), 1.1);\r
+               request.setAccept(newacc);\r
+           } catch (MimeTypeFormatException ex) {\r
+               // not a valid mime type... maybe something else, do not care\r
+           }\r
+       }\r
+       boolean commentOnly = negotiate(request);\r
+       HttpEntityTag etag = null;\r
+       if (commentOnly)\r
+           etag = getComETag();\r
+       else\r
+           etag = getETag();\r
+       // Check validators:\r
+       int cim = checkIfMatch(request, etag);\r
+       if ((cim == COND_FAILED) || (cim == COND_WEAK)) {\r
+           reply = request.makeReply(HTTP.PRECONDITION_FAILED);\r
+           reply.setContent("Pre-conditions failed.");\r
+           reply.setContentMD5(null);\r
+           return reply;\r
+       }\r
+       if ( checkIfUnmodifiedSince(request) == COND_FAILED ) {\r
+           reply = request.makeReply(HTTP.PRECONDITION_FAILED);\r
+           reply.setContent("Pre-conditions failed.");\r
+           reply.setContentMD5(null);\r
+           return reply;\r
+       }\r
+       if ( checkValidators(request, etag) == COND_FAILED ) {\r
+           reply = createDefaultReply(request, HTTP.NOT_MODIFIED);\r
+           reply.setETag(etag);\r
+           reply.setContentMD5(null);\r
+           return reply;\r
+       }\r
+       // Does this file really exists, if so send it back\r
+       if ( file.exists() ) {\r
+           if (commentOnly) {\r
+               reply = createCommentReply(request);\r
+           } else {\r
+               reply = createFileReply(request);\r
+           }\r
+           if (request.hasState(STATE_CONTENT_LOCATION))\r
+               reply.setContentLocation(getURL(request).toExternalForm());\r
+           return reply;\r
+       } else {\r
+           return deleteMe(request);\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Allow PUT based only on ETags, otherwise PUT is done on the image itself\r
+     * @see HTTPFrame.putFileResource\r
+     */\r
+    protected Reply putFileResource(Request request)\r
+       throws ProtocolException, ResourceException\r
+    {\r
+       // check if it is the right resource below!\r
+       if (!(fresource instanceof ImageFileResource)) {\r
+           return super.putFileResource(request);\r
+       }\r
+       Reply reply = null;\r
+       int status = HTTP.OK;\r
+       fresource.checkContent();\r
+       updateCachedHeaders();\r
+       // Is this resource writable ?\r
+       if ( ! getPutableFlag() ) {\r
+           Reply error = request.makeReply(HTTP.NOT_ALLOWED) ;\r
+           error.setContent("Method PUT not allowed.") ;\r
+           throw new HTTPException (error) ;\r
+       }\r
+       HttpEntityTag etag = getComETag();\r
+       // no IfMatch, or no matching ETag, maybe a PUT on the image\r
+       int cim = checkIfMatch(request, etag);\r
+       if ((request.getIfMatch() == null) || \r
+           (cim == COND_FAILED) || (cim == COND_WEAK)) {\r
+           return super.putFileResource(request);\r
+       }\r
+       // check all the others validator\r
+\r
+       // Check remaining validators (checking if-none-match is lame\r
+       // as we already require the If-Match\r
+       if ((checkIfNoneMatch(request, etag) == COND_FAILED)\r
+           || (checkIfModifiedSince(request) == COND_FAILED)\r
+           || (checkIfUnmodifiedSince(request) == COND_FAILED)) {\r
+           Reply r = request.makeReply(HTTP.PRECONDITION_FAILED);\r
+           r.setContent("Pre-condition failed.");\r
+           return r;\r
+       }\r
+       // Check the request:\r
+       InputStream in = null;\r
+       try {\r
+           in = request.getInputStream();\r
+           if ( in == null ) {\r
+               Reply error = request.makeReply(HTTP.BAD_REQUEST) ;\r
+               error.setContent ("<p>Request doesn't have a valid content.");\r
+               throw new HTTPException (error) ;\r
+           }\r
+       } catch (IOException ex) {\r
+           throw new ClientException(request.getClient(), ex);\r
+       }\r
+       // We do not support (for the time being) put with ranges:\r
+       if ( request.hasContentRange() ) {\r
+           Reply error = request.makeReply(HTTP.BAD_REQUEST);\r
+           error.setContent("partial PUT not supported.");\r
+           throw new HTTPException(error);\r
+       }\r
+       // Check that if some type is provided it doesn't conflict:\r
+       if ( request.hasContentType() ) {\r
+           MimeType rtype = request.getContentType() ;\r
+           MimeType type  = getCommentType() ;\r
+           if ( type == null ) {\r
+               setValue (ATTR_CONTENT_TYPE, rtype) ;\r
+           } else if ( rtype.match (type) < 0 ) {\r
+               if (debug) {\r
+                   System.out.println("No match between: ["+\r
+                                      rtype.toString()+"] and ["+\r
+                                      type.toString()+"]");\r
+               }\r
+               Reply error = request.makeReply(HTTP.UNSUPPORTED_MEDIA_TYPE) ;\r
+               error.setContent ("<p>Invalid content type: "+type.toString());\r
+               throw new HTTPException (error) ;\r
+           }\r
+       }\r
+       ImageFileResource ifresource = (ImageFileResource) fresource;\r
+       // Write the body back to the file:\r
+       try {\r
+           // We are about to accept the put, notify client before continuing\r
+           Client client = request.getClient();\r
+           if ( client != null  && request.getExpect() != null ) {\r
+               client.sendContinue();\r
+           }\r
+           if ( ifresource.newMetadataContent(request.getInputStream()) )\r
+               status = HTTP.CREATED;\r
+           else\r
+               status = HTTP.NO_CONTENT;\r
+       } catch (IOException ex) {\r
+           throw new ClientException(request.getClient(), ex);\r
+       }\r
+       if ( status == HTTP.CREATED ) {\r
+           reply = createCommentReply(request, status);\r
+           reply.setContent("<P>Resource succesfully created");\r
+           if (request.hasState(STATE_CONTENT_LOCATION))\r
+               reply.setContentLocation(getURL(request).toExternalForm());\r
+            // Henrik's fix, create the Etag on 201\r
+           if (fresource != null) {\r
+               // We only take car eof etag here:\r
+               if ( etag == null ) {\r
+                   reply.setETag(getComETag());\r
+               }\r
+           }\r
+           reply.setLocation(getURL(request));\r
+           reply.setContent ("<p>Entity body saved succesfully !") ;\r
+       } else {\r
+           reply = createCommentReply(request, status);\r
+       }\r
+       return reply ;\r
+    }  \r
+}\r