Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / jigsaw / src / org / w3c / www / protocol / http / cache / EntityCachedResource.java
diff --git a/JMCR-Stable/real-world application/jigsaw/src/org/w3c/www/protocol/http/cache/EntityCachedResource.java b/JMCR-Stable/real-world application/jigsaw/src/org/w3c/www/protocol/http/cache/EntityCachedResource.java
new file mode 100644 (file)
index 0000000..aaa8119
--- /dev/null
@@ -0,0 +1,857 @@
+// EntityCachedResource.java\r
+// $Id: EntityCachedResource.java,v 1.2 2010/06/15 17:53:00 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.www.protocol.http.cache;\r
+\r
+import java.net.URL;\r
+\r
+import java.util.Enumeration;\r
+\r
+import java.io.BufferedInputStream;\r
+import java.io.FileInputStream;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.io.PrintStream;\r
+\r
+import org.w3c.util.ArrayDictionary;\r
+import org.w3c.tools.resources.Attribute;\r
+import org.w3c.tools.resources.AttributeRegistry;\r
+import org.w3c.tools.resources.BooleanAttribute;\r
+import org.w3c.tools.resources.IntegerAttribute;\r
+import org.w3c.tools.resources.LongAttribute;\r
+import org.w3c.tools.resources.StringAttribute;\r
+import org.w3c.www.http.HeaderDescription;\r
+import org.w3c.www.http.HTTP;\r
+import org.w3c.www.http.HttpCacheControl;\r
+import org.w3c.www.http.HttpContentRange;\r
+import org.w3c.www.http.HttpRange;\r
+import org.w3c.www.http.HttpEntityTag;\r
+import org.w3c.www.http.HttpFactory;\r
+import org.w3c.www.http.ByteRangeOutputStream;\r
+import org.w3c.www.protocol.http.HttpException;\r
+import org.w3c.www.protocol.http.Reply;\r
+import org.w3c.www.protocol.http.Request;\r
+import org.w3c.www.mime.MimeType;\r
+import org.w3c.jigsaw.frames.MimeTypeAttribute;\r
+\r
+/**\r
+ * A cached resource with an entity\r
+ */\r
+public class EntityCachedResource extends CachedResource {\r
+\r
+    /**\r
+     * Condition check return code - Condition existed but failed.\r
+     */\r
+    public static final int COND_FAILED = 1;\r
+    /**\r
+     * Condition check return code - Condition existed and succeeded.\r
+     */\r
+    public static final int COND_OK = 2;  \r
+    /**\r
+     * Condition check return code - Condition existed and succeeded \r
+     *                               but is a weak validation.\r
+     */\r
+    public static final int COND_WEAK = 3;    \r
+\r
+    /**\r
+     * Attribute index - The Content-Type of the resource\r
+     */\r
+    protected static int ATTR_CONTENT_TYPE = -1;\r
+    /**\r
+     * Attribute index - The resource's max age.\r
+     */\r
+    protected static int ATTR_FRESHNESS_LIFETIME = -1;\r
+    /**\r
+     * Attribute index - The initial age of this resource.\r
+     */\r
+    protected static int ATTR_INITIAL_AGE = -1;\r
+    /**\r
+     * Attribute index - The response time\r
+     */\r
+    protected static int ATTR_RESPONSE_TIME = -1;    \r
+    /**\r
+     * Attribute index - Revalidate flag\r
+     */\r
+    protected static int ATTR_REVALIDATE = -1;    \r
+    /**\r
+     * Attribute index - The download state\r
+     */\r
+    protected static int ATTR_LOAD_STATE = -1;    \r
+\r
+    static {\r
+       Attribute a = null;\r
+       Class     c = null;\r
+       try {\r
+           c = Class.forName(\r
+               "org.w3c.www.protocol.http.cache.EntityCachedResource");\r
+           //Added by Jeff Huang\r
+           //TODO: FIXIT\r
+       } catch (Exception ex) {\r
+           ex.printStackTrace();\r
+           System.exit(1);\r
+       }\r
+       // Declare the contenht type attribute:\r
+       a = new MimeTypeAttribute("content-type"\r
+                                 , null\r
+                                 , Attribute.COMPUTED);\r
+       ATTR_CONTENT_TYPE = AttributeRegistry.registerAttribute(c, a);\r
+       // Declare the max-age (freshness lifetime) value.\r
+       a = new IntegerAttribute("freshness-lifetime"\r
+                                , null\r
+                                , Attribute.COMPUTED);\r
+       ATTR_FRESHNESS_LIFETIME = AttributeRegistry.registerAttribute(c, a);\r
+       // Declare the initial age value.\r
+       a = new IntegerAttribute("initial-age"\r
+                                , null\r
+                                , Attribute.COMPUTED);\r
+       ATTR_INITIAL_AGE = AttributeRegistry.registerAttribute(c, a);\r
+       // Declare the response time value.\r
+       a = new LongAttribute("response-time"\r
+                             , null\r
+                             , Attribute.COMPUTED);\r
+       ATTR_RESPONSE_TIME = AttributeRegistry.registerAttribute(c, a);\r
+       // Declare the response time value.\r
+       a = new BooleanAttribute("revalidate"\r
+                             , Boolean.FALSE\r
+                             , Attribute.COMPUTED);\r
+       ATTR_REVALIDATE = AttributeRegistry.registerAttribute(c, a);    \r
+    }\r
+\r
+    // some download specific variables\r
+    protected boolean revalidating = false;\r
+    protected boolean regetting    = false;\r
+    protected boolean hasEntity    = false;\r
+    protected int       oldsize = -1;\r
+    protected int    wantedsize = -1;\r
+    // our cache filter, if we need to notify it\r
+    protected CacheFilter filter;\r
+\r
+    /**\r
+     * Get the Content-Type of the cached resource of <code>null</code> if\r
+     * there is no mime type (it should NEVER happen!)\r
+     * @return a MimeType\r
+     */\r
+    public MimeType getContentType() { \r
+       return (MimeType) getValue(ATTR_CONTENT_TYPE, null);\r
+    }\r
+\r
+    /**\r
+     * Set the Content-Type of this cached resource\r
+     * @param a MimeType, the mime type of this resource\r
+     */\r
+    public void setContentType(MimeType type) {\r
+       setValue(ATTR_CONTENT_TYPE, type);\r
+    }\r
+\r
+    /**\r
+     * Get this resource's freshness lifetime (RFC2616: 13.2.4).\r
+     * @return A long number of seconds for which that entry will remain\r
+     * valid, or <strong>-1</strong> if undefined.\r
+     */\r
+    public int getFreshnessLifetime() {\r
+       return getInt(ATTR_FRESHNESS_LIFETIME, -1);\r
+    }\r
+\r
+    /**\r
+     * Set this cached entry . freshness lifetime (RFC2616: 13.2.4).\r
+     * @param maxage A number of seconds during which the entry will \r
+     * remain valid, or <strong>-1</strong> to undefine previous setting.\r
+     */\r
+    public void setFreshnessLifetime(int freshnessLifetime) {\r
+       setInt(ATTR_FRESHNESS_LIFETIME, freshnessLifetime);\r
+    }\r
+\r
+    /**\r
+     * Get this cached entry initial age.\r
+     * @return A long number of seconds giving the initial age\r
+     * or <strong>-1</strong> if undefined.\r
+     */\r
+    public int getInitialAge() {\r
+       return getInt(ATTR_INITIAL_AGE, -1);\r
+    }\r
+\r
+    /**\r
+     * Set this resource's initial age.\r
+     * @param initage The initial age as a number of seconds\r
+     * or <strong>-1</strong> to undefine previous setting.\r
+     */\r
+    public void setInitialAge(int initage) {\r
+       setInt(ATTR_INITIAL_AGE, initage);\r
+    }\r
+\r
+    /**\r
+     * Get the time of the response used to cached that entry.\r
+     * @return A long number of milliseconds since Java epoch, or <strong>\r
+     * -1</strong> if undefined.\r
+     */\r
+    public long getResponseTime() {\r
+       return getLong(ATTR_RESPONSE_TIME, -1);\r
+    }\r
+\r
+    /**\r
+     * Set this cached entry response time.\r
+     * @param responsetime A long number of milliseconds indicating the \r
+     * response time relative to Java epoch, or <strong>-1</strong> to \r
+     * undefined previous setting.\r
+     */\r
+    public void setResponseTime(long responsetime) {\r
+       setLong(ATTR_RESPONSE_TIME, responsetime);\r
+    }\r
+\r
+    /**\r
+     * Get the revalidate flag\r
+     * @return a boolean, <code>true</code> if the proxy must revalidate\r
+     * stale entries\r
+     * -1</strong> if undefined.\r
+     */\r
+    public boolean getRevalidate() {\r
+       return getBoolean(ATTR_REVALIDATE, false);\r
+    }\r
+\r
+    /**\r
+     * Set this cached entry revalidate flag.\r
+     * @param validate, a boolean, <code>true</code> if this entry needs\r
+     * to be revalidated while stale.\r
+     */\r
+    public void setRevalidate(boolean validate) {\r
+       setBoolean(ATTR_REVALIDATE, validate);\r
+    }\r
+\r
+    /**\r
+     * Get the entity tag associated with that cached entry\r
+     * @return the entity tag or <strong>null</strong> if undefined\r
+     */\r
+    public HttpEntityTag getHETag() {\r
+       if (definesAttribute(ATTR_ETAG)) {\r
+           if (etags == null) {\r
+               etags    = new HttpEntityTag[1];\r
+               etags[0] = HttpFactory.parseETag(getETag());\r
+           }\r
+           return etags[0];\r
+       }\r
+       return null;\r
+    }\r
+    // FIXME add entity tag here\r
+\r
+    // end of the basic accessors\r
+\r
+    /**\r
+     * Get the cached data for that cached entry.\r
+     * @return A <em>non-buffered</em> output stream.\r
+     */\r
+    public synchronized InputStream getInputStream() \r
+       throws IOException\r
+    {\r
+       return new BufferedInputStream(new FileInputStream(getFile()));\r
+    }\r
+\r
+    /**\r
+     * Get the current age of this resource\r
+     * @return a long the current age of this resource\r
+     */\r
+    public int getCurrentAge() {\r
+       long now = System.currentTimeMillis();\r
+       // RFC2616: 13.2.3 Age Calculation\r
+        return (int) (getInitialAge() + ((now - getResponseTime()) / 1000));\r
+    }\r
+\r
+    /**\r
+     * Try to validate an <code>If-Modified-Since</code> request.\r
+     * @param request The request to validate.\r
+     * @return An integer, <code>COND_FAILED</code>, if the condition  was\r
+     * checked, but failed; <code>COND_OK</code> of condition was checked\r
+     * and succeeded, <strong>0</strong> otherwise.\r
+     */\r
+\r
+    public int checkIfModifiedSince(Request request) {\r
+       // Check for an If-Modified-Since conditional:\r
+       long ims = request.getIfModifiedSince();\r
+       long cmt = getLastModified();\r
+       if (ims >= 0) {\r
+           if (cmt > 0) {\r
+               long s_cmt = cmt / 1000;\r
+               long s_ims = ims / 1000;\r
+               if (s_cmt < s_ims) {\r
+                   return COND_FAILED;\r
+               } else if (s_cmt == s_ims) {\r
+                   return COND_WEAK;\r
+               }\r
+               return COND_OK;\r
+           }\r
+       }\r
+       return 0;\r
+    }\r
+\r
+    /**\r
+     * Try to validate an <code>If-Unmodified-Since</code> request.\r
+     * @param request The request to validate.\r
+     * @return An integer, <code>COND_FAILED</code>, if the condition  was\r
+     * checked, but failed; <code>COND_OK</code> of condition was checked\r
+     * and succeeded, <strong>0</strong> otherwise.\r
+     */\r
+\r
+    public int checkIfUnmodifiedSince(Request request) {\r
+       // Check for an If-Unmodified-Since conditional:\r
+       long iums = request.getIfUnmodifiedSince();\r
+       long cmt = getLastModified();\r
+       if ( iums >= 0 ) \r
+           return ((cmt > 0) && (cmt - 1000) >= iums) ? COND_FAILED : COND_OK;\r
+       return 0;\r
+    }\r
+\r
+    /**\r
+     * Try to validate an <code>If-Match</code> request.\r
+     * @param request The request to validate.\r
+     * @return An integer, <code>COND_FAILED</code>, if the condition  was\r
+     * checked, but failed; <code>COND_OK</code> of condition was checked\r
+     * and succeeded, <strong>0</strong> otherwise.\r
+     */\r
+\r
+    public int checkIfMatch(Request request) {\r
+       HttpEntityTag tags[] = request.getIfMatch();\r
+       if ( tags != null ) {\r
+           HttpEntityTag etag = getHETag();\r
+           // Good, real validators in use:\r
+           if ( etag != null ) {\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
+       return 0;\r
+    }\r
+\r
+    /**\r
+     * Try to validate an <code>If-None-Match</code> request.\r
+     * @param request The request to validate.\r
+     * @return An integer, <code>COND_FAILED</code>, if the condition  was\r
+     * checked, but failed; <code>COND_OK</code> of condition was checked\r
+     * and succeeded, <strong>0</strong> otherwise.\r
+     */\r
+\r
+    public int checkIfNoneMatch(Request request) {\r
+       String setag = getETag();\r
+       HttpEntityTag etag = null;\r
+       // Check for an If-None-Match conditional:\r
+       HttpEntityTag tags[] = request.getIfNoneMatch();\r
+       if (setag != null) {\r
+           etag = HttpFactory.parseETag(getETag());\r
+       }\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
+                   if (t.isWeak() || etag.isWeak()) {\r
+                       status = COND_WEAK;\r
+                   } else {\r
+                       return COND_FAILED;\r
+                   }\r
+               }\r
+               if (t.getTag().equals("*")) {\r
+                   return COND_FAILED;\r
+               }\r
+           }\r
+           return status;\r
+       }\r
+       return 0;\r
+    }\r
+\r
+    /**\r
+     * Called when the tee succeed, it allows you to notify a listener of the \r
+     * Tee that the download completed succesfully with a specific size\r
+     * @parameter the size received, an integer\r
+     */\r
+    public synchronized void notifyTeeSuccess(int size) {\r
+       int state = getLoadState();\r
+        try {\r
+           if ( wantedsize > 0 ) {\r
+               if (!regetting) {\r
+                   // sanity check\r
+                   if (state == STATE_NOT_LOADED) {\r
+                       if (size == wantedsize) {\r
+                           // cool! the right size and it was the first \r
+                           // download!\r
+                           setCurrentLength(size);\r
+                           setLoadState(STATE_LOAD_COMPLETE);\r
+                       } else {\r
+                           // argh! wrong size and a success hum...\r
+                           setCurrentLength(size);\r
+                           setLoadState(STATE_LOAD_ERROR);\r
+                           System.out.println(getIdentifier()\r
+                                              +": tee stream mismatch, "\r
+                                              + "bytes(adv/got)="+\r
+                                              wantedsize+"/"+size);\r
+                       }\r
+                   } else {\r
+                       // how can we end up here, I frankly don't know\r
+                       setCurrentLength(size);\r
+                       setLoadState(STATE_LOAD_ERROR);\r
+                       System.out.println(getIdentifier()\r
+                                          +": UNKNOWN STATE for "\r
+                                          +"tee stream!, bytes(adv/got)="+\r
+                                          wantedsize+"/"+size);\r
+                   }\r
+               } else {\r
+                   // we asked for the diff, and we have it!!!\r
+                   if (size == wantedsize) {\r
+                       setCurrentLength(oldsize+wantedsize);\r
+                       setLoadState(STATE_LOAD_COMPLETE);\r
+                   } else {\r
+                       // argh! wrong size and a success hum...\r
+                       setCurrentLength(size);\r
+                       setLoadState(STATE_LOAD_ERROR);\r
+                       System.out.println(getIdentifier()\r
+                                          +": tee stream mismatch in reget, "\r
+                                          + "bytes(adv/got)="+\r
+                                          wantedsize+"/"+size);\r
+                   }\r
+               }\r
+           } else {\r
+               // we didn't knew the size, we should trust what we got\r
+               // (unless it is HTTP/1.0)\r
+               setCurrentLength(size);\r
+               // FIXME (a trust flag to select btw COMPLETE and UNKNOWN\r
+               setLoadState(STATE_LOAD_COMPLETE);\r
+           }\r
+           // Update cache filter space usage:\r
+//         filter.markUsed(this, oldsize, wantedsize);\r
+         \r
+       } finally {\r
+           cleanUpload();\r
+       }\r
+    }\r
+\r
+    public void notifyTeeFailure(int size) {\r
+       System.out.println(getIdentifier()+": tee streaming failed !");\r
+       int state = getLoadState();\r
+       \r
+       setCurrentLength(size);\r
+       setLoadState(STATE_LOAD_ERROR);\r
+       System.out.println(getIdentifier()\r
+                          +": tee stream mismatch, "\r
+                          + "bytes(adv/got)="+\r
+                          wantedsize+"/"+size);\r
+       // and finish the thing!\r
+       cleanUpload();\r
+    }\r
+\r
+    // FIXME should be called after every upload\r
+    protected synchronized void cleanUpload() {\r
+       // FIXME reset a whole bunch of stuff\r
+       uploading = false;\r
+       filter.cleanUpload(this);\r
+       notifyAll();\r
+    }\r
+\r
+    /**\r
+     * FIXME Will be replaced soon, so that multiple people may share \r
+     * the same temporary resource.\r
+     * Wait for the upload to finish, if needed.\r
+     */\r
+    protected final synchronized void waitUpload() {\r
+       while ( uploading ) {\r
+           try {\r
+               wait();\r
+           } catch (InterruptedException ex) {\r
+           }\r
+       }\r
+    }\r
+\r
+    /**\r
+     * handle a range request, according to the first range or the\r
+     * request FIXME we should handle all the ranges at some point...\r
+     */\r
+    protected Reply handleRangeRequest(Request request, HttpRange r) {\r
+       // Should we check against a IfRange header ?\r
+       HttpEntityTag t = request.getIfRange();\r
+       if ( t != null ) {\r
+           if (t.isWeak() || ! t.getTag().equals(getHETag().getTag()))\r
+               return null;\r
+       }\r
+       // Check the range:\r
+       int cl = getContentLength();\r
+       int fb = r.getFirstPosition();\r
+       int lb = r.getLastPosition();\r
+       int sz;\r
+       if (fb > cl-1) { // first byte already out of range\r
+           HttpContentRange cr = HttpFactory.makeContentRange("bytes", 0,\r
+                                                              cl - 1, cl);\r
+           Reply rr;\r
+           rr = request.makeReply(HTTP.REQUESTED_RANGE_NOT_SATISFIABLE);\r
+           rr.setContentLength(-1);\r
+           rr.setHeaderValue(rr.H_CONTENT_RANGE, cr);\r
+           rr.setContentMD5(null);\r
+           return rr;\r
+       }\r
+       if ((fb < 0) && (lb >= 0)) { // ex: bytes=-20 final 20 bytes\r
+           if (lb >= cl)   // cut the end\r
+               lb = cl;\r
+           sz = lb;\r
+           fb = cl - lb;\r
+           lb = cl - 1;\r
+       } else if (lb < 0) {  // ex: bytes=10- the last size - 10\r
+           lb = cl-1;\r
+           sz = lb-fb+1;\r
+       } else {              // ex: bytes=10-20\r
+           if (lb >= cl)  // cut the end\r
+               lb = cl-1;\r
+           sz = lb-fb+1;\r
+       }\r
+       if ((fb < 0) || (lb < 0) || (fb <= lb)) {\r
+           HttpContentRange cr = null;\r
+           fb = (fb < 0) ? 0 : fb;\r
+           lb = ((lb > cl) || (lb < 0)) ? cl : lb;\r
+           cr = HttpFactory.makeContentRange("bytes", fb, lb, cl);\r
+           // Emit reply:\r
+           Reply rr = request.makeReply(HTTP.PARTIAL_CONTENT);\r
+           try {\r
+               rr.setContentMD5(null); // just in case :)\r
+               rr.setContentLength(sz);\r
+               rr.setHeaderValue(rr.H_CONTENT_RANGE, cr);\r
+               rr.setStream(new ByteRangeOutputStream(getFile(), fb, lb+1));\r
+               return rr;\r
+           } catch (IOException ex) {\r
+           }\r
+       } \r
+       return null;\r
+    }\r
+       \r
+    /**\r
+     * decorate the reply header with some meta information taken\r
+     * from the cached resource\r
+     * @return a reply, the one we just updated\r
+     */\r
+    protected Reply setReplyHeaders(Reply reply) {\r
+       int status = reply.getStatus();\r
+       if (status != HTTP.NOT_MODIFIED) {\r
+           // FIXME check for byte range replies\r
+           reply.setContentLength(getContentLength());\r
+           // dump the headers we know.\r
+           reply.setContentMD5(getContentMD5());\r
+           reply.setContentLanguage(getContentLanguage());\r
+           reply.setContentEncoding(getContentEncoding());\r
+           reply.setContentType(getContentType());\r
+           reply.setLastModified(getLastModified());\r
+           reply.setVary(getVary());\r
+       }\r
+       reply.setETag(getHETag());\r
+       long date = getDate();\r
+       if ( date > 0 )\r
+           reply.setDate(getDate());\r
+       reply.setAge(getCurrentAge());\r
+       ArrayDictionary a = getExtraHeaders();\r
+       if ( a != null ) {\r
+           // This is the slowest operation of the whole cache :-(\r
+           Enumeration e = a.keys();\r
+           while (e.hasMoreElements() ) {\r
+               String hname  = (String) e.nextElement();\r
+               String hvalue = (String) a.get(hname);\r
+               reply.setValue(hname, hvalue);\r
+           }\r
+       }\r
+       if ((filter != null) && filter.isShared()) {\r
+           HttpCacheControl hcc = reply.getCacheControl();\r
+           if (hcc != null) {\r
+               String priv[] = hcc.getPrivate();\r
+               if (priv != null) {\r
+                   for (int i=0; i<priv.length; i++) {\r
+                       // remove headers that are private if we are\r
+                       // a shared cache (rfc2616#14.9, rfc2616#14.9.1)\r
+                       reply.setHeaderValue(priv[i], null);\r
+                   }\r
+               }\r
+           }\r
+       }\r
+       return reply;\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) {\r
+       int v_inm = checkIfNoneMatch(request);\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_FAILED;\r
+       }\r
+       return 0;\r
+    }\r
+\r
+    /**\r
+     * This cached entry has been checked valid, perform given request.\r
+     * @param request The request to perform.\r
+     * @return An Reply instance.\r
+     * @exception HttpException If something went wrong.\r
+     */\r
+    public Reply perform(Request request)\r
+       throws HttpException\r
+    {\r
+       // If the resource is currently being uploaded, wait:\r
+       waitUpload();\r
+       // Now perform the request:\r
+       try {\r
+           Reply   reply       = null;\r
+           boolean needsEntity = true;\r
+           // Handle range requests:\r
+           HttpRange ranges[] = request.getRange();\r
+           if ((ranges != null) && (ranges.length == 1)) \r
+               reply = handleRangeRequest(request, ranges[0]);\r
+           // Handle full retreivals:\r
+           if ( reply == null ) {\r
+               int status = getStatus();\r
+               // Try validating first \r
+               // NOTE: We know we are only dealing with GETs and HEADs here\r
+               // otherwise the cache wouldn't be used...\r
+               int cim = checkIfMatch(request);\r
+               if ( (cim == COND_FAILED) || (cim == COND_WEAK) ) {\r
+                   status      = HTTP.PRECONDITION_FAILED;\r
+                   needsEntity = false;\r
+                   reply = request.makeReply(status);\r
+                   reply.setContent("Pre-conditions failed.");\r
+                   throw new HttpException(request, reply, "pre-condition");\r
+               } else if ( checkIfUnmodifiedSince(request) == COND_FAILED ) {\r
+                   status      = HTTP.PRECONDITION_FAILED;\r
+                   reply = request.makeReply(status);\r
+                   reply.setContent("Pre-conditions failed.");\r
+                   throw new HttpException(request, reply, "pre-condition");\r
+               } else if ( checkValidators(request) == COND_FAILED ) {\r
+                   status      = HTTP.NOT_MODIFIED;\r
+                   needsEntity = false;\r
+               }\r
+               // Emit reply:\r
+               reply = request.makeReply(status);\r
+               if ( needsEntity ) {\r
+                   reply.setStream(getInputStream());\r
+               }\r
+           }\r
+           setReplyHeaders(reply);\r
+           // Check if entity is needed:\r
+           String mth = request.getMethod();\r
+           if ( mth.equals("HEAD") || mth.equals("OPTIONS") )\r
+               reply.setStream(null);\r
+//         filter.markUsed(this);\r
+           return reply;\r
+       } catch (IOException ex) {\r
+//         if (debug)\r
+//             ex.printStackTrace();\r
+           // Some exception occured, delete that resource (no longer usefull)\r
+//         delete();\r
+       }\r
+       return null;\r
+    }\r
+\r
+    /**\r
+     * Try using an active stream to cache the content.\r
+     * Byte size usage is taken care of only at the end of the download\r
+     * to make sure we get the right sizes (might different from the\r
+     * advertized ones).\r
+     * @return An InputStream instance if active caching was possible,\r
+     * <strong>null</strong> otherwise.\r
+     */\r
+    public synchronized InputStream tryActiveCacheContent(InputStream in)\r
+       throws IOException\r
+    {\r
+       // If we don't return null, we *are* responsible for cleaning up\r
+       // the upload *whatever* happens ...\r
+       InputStream  tee = null;\r
+       OutputStream out = null;\r
+       uploading = true;\r
+       // Open the output stream:\r
+       try {\r
+           out = new FileOutputStream(getFile());\r
+       } catch (IOException ex) {\r
+//         if (debug)\r
+               ex.printStackTrace();\r
+           // We'll let cacheContent take care of that situation:\r
+           return null;\r
+       }\r
+       // We might be able to use active streams:\r
+//     if (upnewsize > ACTIVE_STREAM_THRESOLD ) {\r
+           tee = ActiveStream.createTee(this, in, out);\r
+           if ( tee != null )\r
+               return tee;\r
+//     } \r
+       // We were not able to active stream:\r
+       try {\r
+           out.close();\r
+       } catch (IOException ex) {\r
+       }\r
+       return null;\r
+    }\r
+\r
+    /**\r
+     * The basic initialization\r
+     */\r
+    public void initialize(Object values[]) {\r
+       super.initialize(values);\r
+    }    \r
+\r
+    /**\r
+     * sets some useful information about the entity\r
+     * @param the request that requested this entity\r
+     * @param the reply triggered by this request\r
+     */\r
+    protected void updateInfo(Request request, Reply rep) {\r
+       String   mth       = request.getMethod();\r
+       Reply    reply     = (Reply) rep.getClone();\r
+       boolean  hasEntity = !(mth.equals("HEAD") || mth.equals("OPTIONS"));\r
+       // is it a revalidation?\r
+       if (!request.hasState(CacheState.STATE_REVALIDATION)) {\r
+           // no, go for it!\r
+           HttpCacheControl hcc = reply.getCacheControl();\r
+           // first we should NOT cache headers protected by a no-cache\r
+           // per rfc2616@14.9\r
+           if (hcc != null) {\r
+               String nocache[] = hcc.getNoCache();\r
+               if (nocache != null) {\r
+                   for (int i=0; i< nocache.length; i++) {\r
+                       reply.setHeaderValue(nocache[i], null);\r
+                   }\r
+               }\r
+           }\r
+           setStatus(reply.getStatus());\r
+           setContentType(reply.getContentType());\r
+           setContentLength(reply.getContentLength());\r
+           setLastModified(reply.getLastModified());\r
+           setContentMD5(reply.getContentMD5());\r
+           String vary[] = reply.getVary();\r
+           setVary(vary);\r
+           if (vary != null) {\r
+               // update the conneg headers\r
+               ArrayDictionary a = null;\r
+               for (int i=0; i< vary.length; i++) {\r
+                   if (vary[i].equals("*")) {\r
+                       continue;\r
+                   }\r
+                   if (a == null) {\r
+                       a = new ArrayDictionary(vary.length);\r
+                   }\r
+                   a.put (vary[i].toLowerCase(), request.getValue(vary[i]));\r
+               }\r
+               // FIXME we should be able to update to save multiple\r
+               // matches, but with a limitation of course\r
+               if (a != null) {\r
+                   setConnegHeaders(a);\r
+               }\r
+           }\r
+           if (reply.hasHeader(reply.H_ETAG)) {\r
+               setETag(reply.getETag().toString());\r
+           } else {\r
+               // be safe here!\r
+               setETag(null);\r
+           }\r
+           ArrayDictionary a = new ArrayDictionary(5, 5);\r
+           Enumeration     e = reply.enumerateHeaderDescriptions();\r
+           while ( e.hasMoreElements() ) {\r
+               HeaderDescription d = (HeaderDescription) e.nextElement();\r
+               // Skip all well-known headers:\r
+               if ( d.isHeader(Reply.H_CONTENT_TYPE)\r
+                    || d.isHeader(Reply.H_CONTENT_LENGTH)\r
+                    || d.isHeader(Reply.H_LAST_MODIFIED)\r
+                    || d.isHeader(Reply.H_ETAG)\r
+                    || d.isHeader(Reply.H_AGE)\r
+                    || d.isHeader(Reply.H_DATE)\r
+                    || d.isHeader(Reply.H_VARY)\r
+                    || d.isHeader(Reply.H_CONNECTION)\r
+                    || d.isHeader(Reply.H_PROXY_CONNECTION)\r
+                    || d.isHeader(Reply.H_TRANSFER_ENCODING)\r
+                    || d.isHeader(Reply.H_CONTENT_MD5)\r
+                    || d.getName().equalsIgnoreCase("keep-alive"))\r
+                   continue;\r
+               // This is an extra header:\r
+               a.put(d.getName(), reply.getValue(d));\r
+           }\r
+           setExtraHeaders(a);\r
+           // FIXME add the headers ;)\r
+           \r
+       }\r
+    }\r
+\r
+    /**\r
+     * This cached entry needs revalidation, it will modify the \r
+     * request to do that.\r
+     */\r
+    public Request setRequestRevalidation(Request request) {\r
+       Request origreq = (Request) request.getClone();\r
+       request.setState(CacheState.STATE_RESOURCE, this);\r
+       request.setState(CacheState.STATE_ORIGREQ, origreq);\r
+       // At this point, we use the suggested way of using date as etag:\r
+       request.setIfModifiedSince(getLastModified());\r
+       // But if we do have an etag, we also uses it, as recommended:\r
+       if ((etags == null) && (getETag() != null)) {\r
+           etags    = new HttpEntityTag[1];\r
+           etags[0] = HttpFactory.parseETag(getETag());\r
+       }\r
+       request.setIfNoneMatch(etags);\r
+       // We have to remove all other conditionals here:\r
+       request.setIfRange(null);\r
+       request.setRange(null);\r
+       request.setIfUnmodifiedSince(-1);\r
+       request.setIfMatch(null);\r
+       return request;\r
+    }  \r
+\r
+    /**\r
+     * A constructor for new resources that will get some data\r
+     * directly\r
+     * FIXME params\r
+     */\r
+    public EntityCachedResource(CacheFilter filter, Request req, Reply rep) {\r
+       invalidated = false;\r
+       setValue(ATTR_IDENTIFIER, req.getURL().toExternalForm());\r
+       // Keep fast track of the filter:\r
+       this.filter = filter;\r
+       // update the headers\r
+       updateInfo(req, rep);\r
+       // and do some calculation according to the validator\r
+       filter.getValidator().updateExpirationInfo(this, req, rep);\r
+       // Save the content of resource into the content cache:\r
+       setFile(filter.getStore().getNewEntryFile());\r
+       wantedsize = rep.getContentLength();\r
+       InputStream in;\r
+       try {\r
+           in = tryActiveCacheContent(rep.getInputStream());\r
+           if (in == null) {\r
+               // something bad happened\r
+               // in = cacheContent(reply.getInputStream());\r
+           }\r
+           rep.setStream(in);\r
+       } catch (IOException ex) {\r
+           // FIXME\r
+       }       \r
+    }\r
+\r
+    public EntityCachedResource() {\r
+       super();\r
+    }\r
+}\r
+\r
+\r