home *** CD-ROM | disk | FTP | other *** search
/ PC World 2001 April / PCWorld_2001-04_cd.bin / Software / TemaCD / smartcache / src / cacheobject.java < prev    next >
Encoding:
Java Source  |  2001-01-26  |  24.0 KB  |  889 lines

  1. /*
  2.  *  Smart Cache, http proxy cache server
  3.  *  Copyright (C) 1998, 1999, 2000 Radim Kolar 
  4.  *
  5.  *    Smart Cache is Open Source Software; you may redistribute it
  6.  *  and/or modify it under the terms of the GNU General Public
  7.  *  License as published by the Free Software Foundation; either
  8.  *  version 2, or (at your option) any later version.
  9.  *
  10.  *    This program distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13.  *  General Public License for more details.
  14.  *
  15.  *    A copy of the GNU General Public License is available as
  16.  *  /usr/doc/copyright/GPL in the Debian GNU/Linux distribution or on
  17.  *  the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
  18.  *  can also obtain it by writing to the Free Software Foundation,
  19.  *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20.  */
  21.  
  22. import java.io.*;
  23. import java.util.*;
  24. import java.util.zip.*;
  25. import java.net.*;
  26.  
  27. public final class cacheobject{
  28.  
  29. public static final String RESERVED="<>";
  30. /* statistics */
  31. public static int c_hit,c_miss,c_refresh,c_block;
  32. public static int b_hit,b_miss;
  33.  
  34. /* static ! */
  35. public static boolean end_dot;
  36. public static boolean keep_deleted;
  37. public static int generate_lastmod;
  38. public static boolean hide_errors;
  39. public static boolean escape_backslash;
  40. public static String custom500;
  41. public static String defaultname;
  42. public static boolean auto_compress;
  43. public static boolean auto_decompress;
  44.  
  45. private String name;      /* real name of object */
  46. private String localname; /* stored in file name */
  47. private String location; /* for redirects */
  48.  
  49. private int size,httprc;
  50. private long expires, date, lastmod, lru;
  51. private cachedir dir;
  52. private String ctype;    /* Content-Type */
  53. private String encoding; /* Content-Encoding */
  54. private boolean good; /* good or aborted download ? */
  55.  
  56. cacheobject(String name, cachedir where)
  57. {
  58.  this.name=name;
  59.  dir=where;
  60. }
  61.  
  62. cacheobject(String name,cachedir where,String ln,long lm,String ct, String enc,int size)
  63. {
  64.  //if(name.equals(mgr.DEFAULTNAME)) name="";
  65.  this(name,where);
  66.  localname=ln;
  67.  date=lastmod=lm;
  68.  location=null;
  69.  lru=System.currentTimeMillis();
  70.  ctype=ct;
  71.  encoding=enc;
  72.  this.size=size;
  73.  httprc=200;
  74.  // dir.setDirty();
  75.  good=true;
  76. }
  77.  
  78. final public boolean equals(Object o)
  79. {
  80.  if(o==null || ! (o instanceof cacheobject)) return false;
  81.  cacheobject o1=(cacheobject)o;
  82.  
  83.  return name.equals(o1.getName());
  84. }
  85.  
  86. final public int hashCode()
  87. {
  88.  return name.hashCode();
  89. }
  90.  
  91. final public boolean needSave()
  92. {
  93.  if(localname!=null) return true;
  94.   else
  95.  return false;
  96. }
  97.  
  98. final public String getName()
  99. {
  100.  return name;
  101. }
  102. cacheobject(DataInputStream is, cachedir d,byte version) throws IOException
  103. {
  104.  this.dir=d;
  105.  name=is.readUTF();
  106.  ctype=is.readUTF();
  107.  localname=is.readUTF();
  108.  location=is.readUTF();
  109.  if(location.length()==0) location=null;
  110.  size=is.readInt();
  111.  httprc=is.readInt();
  112.  expires=is.readLong();
  113.  date=is.readLong();
  114.  lastmod=is.readLong();
  115.  lru=is.readLong(); 
  116.  if(version==3)
  117.  {
  118.   encoding=is.readUTF();
  119.   if(encoding.length()==0) encoding=null;
  120.   good=is.readBoolean();
  121.  } else good=true;
  122. }
  123.  
  124. final public void save(DataOutputStream os) throws IOException
  125. {
  126.  if(localname==null) return;
  127.  
  128.  os.writeUTF(name);
  129.  os.writeUTF(ctype);
  130.  os.writeUTF(localname); 
  131.  if(location!=null) os.writeUTF(location); 
  132.   else 
  133.     os.writeUTF("");
  134.  os.writeInt(size);
  135.  os.writeInt(httprc);
  136.  os.writeLong(expires);
  137.  os.writeLong(date); 
  138.  os.writeLong(lastmod); 
  139.  os.writeLong(lru);
  140.  if(encoding!=null) os.writeUTF(encoding); else os.writeUTF("");
  141.  os.writeBoolean(good);
  142. }
  143.  
  144. final public void make_request(request r, long reload_age,long min_age, long max_age, float percent,long expire_age,long redir_age) throws IOException
  145. {
  146.  lru=System.currentTimeMillis();
  147.  
  148.  /* novy objekt */
  149.  if(localname==null) { load_object(r); return;}
  150.  
  151.  /* test na expiraci */
  152.  if(needRefresh(r,reload_age,min_age,max_age,percent,expire_age,redir_age)) { refresh_object(r); return;}
  153.  
  154.  /* jinak poslat z cache */
  155.  send_fromcache(r);
  156. }
  157.  
  158.  
  159. final private File genTmp()
  160. {
  161.  File out;
  162.  String ld=dir.getLocalDir();
  163.  if(ld==null) return null;
  164.  while(true)
  165.  {
  166.   out=new File(ld+(int)(Math.random()*100000000f)+".tmp");
  167.   if(!out.exists()) break;
  168.  }
  169.  return out;
  170. }
  171.  
  172. final public void compress(int level)
  173. {
  174.  if(level<=0) return;
  175.  if(good==false) return;
  176.  if(encoding!=null) return; /* compressed! */
  177.  if(!ctype.startsWith("text/")) return; /* not text */
  178.  if(localname==null || localname.equals(RESERVED)) return;
  179.  if(size<=512) return; /* waste of CPU cycles */
  180.  File t=genTmp();
  181.  int i;
  182.  try
  183.  {
  184.   GZIPOutputStream gz=new GZIPOutputStream(new FileOutputStream(t),4096);
  185.   DataInputStream sin=new DataInputStream(new BufferedInputStream(
  186.   new FileInputStream(dir.getLocalDir()+localname),4096));
  187.   byte b[]=new byte[4096];
  188.   while(true)
  189.   {
  190.    /* precist */
  191.   i=sin.read(b);
  192.   if(i==-1) break;
  193.   gz.write(b,0,i);
  194.   }
  195.  
  196.   gz.close();
  197.   sin.close();
  198.   int sz;
  199.   sz=(int)t.length();
  200.   if(sz>=size-512) { t.delete();return;}
  201.  
  202.   String nm; /* new name */
  203.   if(!localname.endsWith(".gz")) 
  204.       if(localname.endsWith("_r"))
  205.           nm=genName(localname.substring(0,localname.length()-2)+".gz");
  206.         else
  207.           nm=genName(localname+".gz");
  208.    else
  209.      nm=localname;
  210.       
  211.   File n=new File(dir.getLocalDir()+nm);
  212.   /* nm=genName(name)+".gz";
  213.   if(n.exists() && !nm.equals(localname))
  214.   {  // regenerate name to avoid clashes 
  215.     nm=genName(nm.substring(1));
  216.     n=new File(dir.getLocalDir()+nm);
  217.     System.out.println("Regenerating name to "+n);
  218.   }
  219.   */
  220.   /* rename to new */
  221.   if(!rename(t,n)) { 
  222.     System.out.println("**COMPRESS ERROR** Rename "+t+" to "+n+" failed.");
  223.                        dir.dirty=true;
  224.                return;
  225.            }
  226.   encoding="gzip";
  227.   if(!localname.equals(nm)) new File(dir.getLocalDir()+localname).delete();
  228.   localname=nm;
  229.   date=System.currentTimeMillis();
  230.   dir.dirty=true;
  231.   System.out.println("Compressed: "+dir.getLocalDir()+localname+" ("+(size-sz)+" B, "+(int)(100*(1.0f-(float)sz/size))+"% saved)");
  232.   size=sz;
  233.  }
  234.  catch (IOException j) {System.out.println("**COMPRESS ERROR** "+j);}
  235. }
  236.  
  237.  
  238. final public boolean isValid()
  239. {
  240.   if(localname==null || localname.equals(RESERVED)) 
  241.   {
  242.     System.out.print("NULL filename");
  243.     return false;
  244.   }
  245.   File f=new File(  dir.getLocalDir()+localname );
  246.   if(!f.isFile()) 
  247.   {
  248.     System.out.print("No such file");
  249.     return false;
  250.   }
  251.   if(!f.canRead()) {
  252.     System.out.print("No read access");
  253.     return false;
  254.   }
  255.   // check obj. size
  256.   if(f.length()!=size) {
  257.     System.out.print("File size mismatch");
  258.     return false;
  259.   }
  260.   //check obj. date
  261.   if(f.lastModified()>date) {
  262.     System.out.print("Modified after load");
  263.     return false;
  264.   }
  265.   return true; // O.K. !
  266. }
  267.  
  268. final public String getLocalName()
  269. {
  270.  return localname;
  271. }
  272.  
  273. final public void clearLocalName()
  274. {
  275.  if(localname==null) return;
  276.  new File(dir.getLocalDir()+localname).delete();
  277.  localname=null;
  278.  dir.dirty=true;
  279. }
  280.  
  281. final public synchronized void regenName()
  282. {
  283.  if(localname==null) 
  284.  {
  285.    System.out.println("[DEBUG] regen name called on "+name+" with NULL localname!");
  286.  }
  287.  localname=genName(name);
  288. if(encoding==null) return;
  289.  
  290. if(!localname.endsWith(".gz")) 
  291.       if(localname.endsWith("_r"))
  292.           localname=genName(localname.substring(0,localname.length()-2)+".gz");
  293.         else
  294.           localname=genName(localname+".gz");
  295. }
  296. /* Generate unique filename */
  297. final public synchronized String genName(String tmp)
  298. {
  299. /* String tmp;
  300.  tmp=name;
  301.  */
  302.  File f;
  303.  String localdir=dir.getLocalDir();
  304.  
  305.  if(localdir==null) return null;
  306.  // System.err.println("in="+name);
  307.    int j=tmp.length()-1;
  308.    if(j>=0)
  309.    {
  310.      byte v[];
  311.      v=new byte[j+1];
  312.      tmp.getBytes(0,j+1,v,0);
  313.      /* jestlize mame ? a nejaky bordel za nim, uriznout! */
  314.      loop1:for(int zz=0;zz<=j;zz++)
  315.        {
  316.         switch(v[zz])
  317.          {
  318.           case 0x3b: // ;
  319.           case 0x3a: // :
  320.           case 0x3d: // =
  321.           case 0x3f: // ?
  322.           tmp=tmp.substring(0,zz);
  323.           // System.err.println("new tmp="+tmp);
  324.           break loop1;
  325.          }
  326.        }
  327.    }
  328.  // System.err.println("in2="+tmp);   
  329.  
  330.  /* pokud je toto redirect, pridat k jmenu _r */
  331.  /* aby se to nemotalo do jmena adresare */
  332.  if(httprc==301 || httprc==302 || httprc==307) tmp+="_r";
  333.  
  334.   /* nahrazujeme nepratelske znaky */
  335.   // tmp=tmp.replace(':','_'); neni uz nutne
  336.   tmp=tmp.replace('>','}');  
  337.   tmp=tmp.replace('<','{');
  338.   // problems with OS/2
  339.   tmp=tmp.replace('|','!');
  340.   if(escape_backslash) tmp=tmp.replace('\\','-');
  341.   if(tmp.length()==0) tmp=defaultname;
  342.   
  343.   /* Kill tecku if nedovoleno ! */
  344.   if(!end_dot && tmp.endsWith(".")) tmp=tmp.substring(0,tmp.length()-1);
  345.   
  346.   f=new File(localdir+tmp);  
  347.   if(!f.exists()) return tmp;
  348.   /* a nyni kolotoc! */
  349.   int i=0;
  350.   while(true)
  351.   {
  352.    f=new File(localdir+i+tmp);
  353.    if(!f.exists()) break;
  354.    i++;
  355.    // if(i==1000) return null; CHECK: need to check??
  356.   } 
  357.   return i+tmp;
  358. }
  359.  
  360.  
  361. /* L O A D ! */
  362.  
  363. final private void load_object(request r) throws IOException
  364. {
  365.  c_miss++;
  366.  if(localname==null) localname=RESERVED; /* prevents from async dir cleaning */
  367.  if(ctype==null) ctype="unknown/unknown";
  368.  // httprc=500;
  369.  String ln;
  370.  r.add_ims();
  371.  Socket toserver=null;
  372.  dir.dirty=true;
  373.  try{
  374.  toserver=r.connectToHost();
  375.  }
  376.  catch (IOException ccc) { if(localname!=null && localname.equals(RESERVED)) localname=null;
  377.                             dir.dirty=true;
  378.                             /* POSLAT NOT MODIFIED! */
  379.                             if(r.getIms()>0) { r.make_headers(304,null,null,null,0,0,0);
  380.                                                r.send_headers();
  381.                                                return;
  382.                                               } 
  383.                                               
  384.                           /* 500 RC komedie */                    
  385.                           if(custom500==null) r.send_error(500,"Proxy load failed ("+ccc+") and object not found in cache");
  386.                               else
  387.               if(custom500.charAt(0)=='0') {
  388.                           r.make_headers(200,"image/gif",null,null,43,886828316241L,0);
  389.                       r.send_headers();
  390.                       r.sendBytes(r.GIF);
  391.           }     else
  392.                           if(custom500.charAt(0)=='-') { 
  393.                                                      r.send_error(204,"No Content");
  394.                                                     }
  395.                           else if(custom500.charAt(0)=='!')
  396.                           {
  397.                            r.make_headers(200,"text/html",null,null,0,new Date().getTime(),0);
  398.                            r.send_headers();
  399.                            r.sendString("<HTML></HTML>");
  400.                           }                                       
  401.                         else
  402.                         { r.make_headers(301,null,null,custom500,0,new Date().getTime(),0);r.send_headers();}
  403.                 
  404.                        r.close();
  405.                        return;
  406.                       }
  407.                            
  408.  DataInputStream sin=new DataInputStream (new BufferedInputStream(toserver.getInputStream()));
  409.  DataOutputStream sou=new DataOutputStream(new BufferedOutputStream(toserver.getOutputStream()));
  410.  try
  411.  {
  412.  r.send_request(sou);
  413.  r.read_headers(sin);
  414.  }
  415.  catch (IOException e) 
  416.   {
  417.      if(localname!=null && localname.equals(RESERVED)) localname=null;
  418.      dir.dirty=true;
  419.      System.out.println("FAILED: "+dir.getLocalDir()+name+" ("+e+")");
  420.      throw e;
  421.   }
  422.  r.send_headers();
  423.  httprc=r.getRc();
  424.  if(httprc==304) {  /* avoid another Netscape BUG */
  425.                      if(localname!=null && localname.equals(RESERVED)) localname=null;
  426.                      dir.dirty=true;
  427.              toserver.close();
  428.              return;
  429.          }
  430.  
  431.  File f=null;
  432.  switch(httprc)
  433.  {
  434.         /* good RC */
  435.     case 200:        /* OK */
  436.     case 203:        /* Non-Authoritative Information */
  437.     case 300:        /* Multiple Choices */
  438.     case 301:               /* Moved Permanently */
  439.         case 302:        /* Moved Temporary */
  440.     case 307:        /* temporary redirect */
  441.     case 410:        /* Gone */
  442.     /* bad RC */
  443.     case 400:        /* Bad request */
  444.     case 404:        /* not found */
  445.     case 403:        /* Forbidden */
  446.     case 405:        /* method not allowed */
  447.                  f=genTmp();
  448.          break;
  449.     default:
  450.              // System.out.println("[debug] RC="+httprc+" on "+dir.getLocalDir()+name);
  451.  }
  452.  if(!r.canCache()) f=null; /* non cacheable request */
  453.  DataOutputStream fout=null; 
  454.  
  455.  try{
  456.  if(f!=null) fout=new DataOutputStream(
  457.                        new BufferedOutputStream(new FileOutputStream(f),4096));
  458.              else
  459.                {  /* not caching */
  460.            fout=null;
  461.                if(localname!=null && localname.equals(RESERVED)) localname=null;
  462.            dir.dirty=true;
  463.            }
  464.  }
  465.  catch (IOException e) 
  466.            { /* tmp file create error */
  467.          fout=null;
  468.              if(localname!=null && localname.equals(RESERVED)) localname=null;
  469.          dir.dirty=true;
  470.          }
  471.  
  472.  try{
  473.  r.transfer_object(sin,fout,dir);
  474.  }
  475.  catch (IOException e)
  476.   {
  477.    if(f!=null) f.delete();
  478.    if(localname!=null && localname.equals(RESERVED)) localname=null;
  479.    dir.dirty=true;
  480.    System.out.println("FAILED: "+dir.getLocalDir()+name+" ("+e+")");
  481.    throw e;
  482.   }
  483.  finally
  484.   {
  485.     b_miss+=r.getCsize();
  486.   }
  487.  // sin.close();sou.close(); - not needed
  488.  toserver.close();
  489.  if(f==null)  /* not cached, end. */
  490.         {
  491.          if(localname!=null && localname.equals(RESERVED)) localname=null;
  492.      dir.dirty=true;
  493.      return;
  494.     }
  495.  /* multiple downloads are active ? (stupid Netscape bug) */
  496.  if(localname==null || localname.equals(RESERVED)) ln=genName(name);
  497.    else
  498.     ln=localname;
  499.  File n=new File(dir.getLocalDir()+ln);
  500.  if(!rename(f,n)) { 
  501.                     System.out.println("**LOAD ERROR** Rename "+f+" to "+n+" failed.");
  502.                     if(localname!=null && localname.equals(RESERVED)) 
  503.             {
  504.              localname=null;
  505.              n.delete();
  506.             }
  507.             dir.dirty=true;
  508.             f.delete();
  509.             return;
  510.           }
  511.  /* update object status */
  512.  localname=ln;
  513.  setInfo(r);
  514.  System.out.println("Loaded: "+n+" (size="+size+" B)");
  515.  good=true;
  516.  if(auto_compress) compress(9);
  517. }
  518.  
  519. /* R E F R E S H ! */
  520.  
  521. final private void refresh_object(request r) throws IOException
  522. {
  523.  Socket toserver=null;
  524.  dir.dirty=true; 
  525.  try{
  526.  toserver=r.connectToHost();
  527.  }
  528.  catch (IOException eee) { send_fromcache(r);return;}
  529.  
  530.  long clims=r.getIms();
  531.  if(clims==0) clims=1; /* nema nic, to je to same jako kdyby mel hodne starou verzi */
  532.                        /* pokud by se nechalo na 0 - nedostal by OBJEKT! */
  533.  if (clims>=lastmod) clims=0; /* ma pozdejsi verzi objektu nez my, posuzujeme to jako
  534.                                  kdyby mel tu samou jako my, protoze IMS delame na nas objekt */
  535.  r.add_ims(lastmod);
  536.   
  537.  DataInputStream sin=new DataInputStream (new BufferedInputStream(toserver.getInputStream()));
  538.  DataOutputStream sou=new DataOutputStream(new BufferedOutputStream(toserver.getOutputStream()));
  539.  
  540.  try{
  541.  r.send_request(sou);
  542.  r.read_headers(sin);
  543.  }
  544.  catch (IOException e)
  545.    {send_fromcache(r); return;}
  546.    
  547.  /* podivame se co prislo, pokud !=304, tak to posleme a nacachujeme */
  548.  int rc=r.getRc(); 
  549.  if(rc==304) { 
  550.                date=System.currentTimeMillis();
  551.                dir.dirty=true;
  552.                toserver.close();
  553.                expires=r.getExpires(); // refresh possible Expires for Microsoft IIS 4.0
  554.                System.out.println("Checked: "+dir.getLocalDir()+localname);
  555.                if(clims>0) { /* musime poslat objekt z cache */
  556.                              r.clearIms();
  557.                              send_fromcache(r);
  558.                              return;}
  559.                r.send_headers();     
  560.                c_hit++;                         
  561.                return;
  562.              }
  563.   c_refresh++;
  564.  if(httprc<400 && rc>=400) /* we have a GOOD object in cache, but got a wrong reply */
  565.  { 
  566.  
  567.  /* handle object deletion  */ 
  568.  if(rc==404 )
  569.     if(keep_deleted==false) { 
  570.                // nothing special is required
  571.                /*
  572.                new File(dir.getLocalDir()+localname).delete();
  573.                System.out.println("Deleted: "+dir.getLocalDir()+localname);
  574.                localname=null;
  575.                dir.dirty=true;
  576.            */
  577.               }
  578.     else
  579.      {
  580.       System.out.println("Gone: "+dir.getLocalDir()+localname+" - sending cached copy");
  581.       touch();
  582.       try{
  583.           toserver.close();
  584.          }
  585.       catch (IOException e) {}
  586.          
  587.       send_fromcache(r);
  588.       return;
  589.      }
  590.  /* handle temporary remote server error */
  591.  if(rc==400 || rc==403 || rc==408 || rc==405 /* timeout */ || rc>= 500) 
  592.  {
  593.    if(rc<408) touch();
  594.    if (hide_errors==true)
  595.    {
  596.       System.out.println("Error "+rc+":"+dir.getLocalDir()+localname+" - sending cached copy");
  597.       try{
  598.            toserver.close();
  599.          }
  600.       catch (IOException e) {}
  601.       send_fromcache(r);
  602.       return;
  603.    }
  604.    else /* hide errors == false , do not cache this ! */
  605.    {
  606.       System.out.println("Error "+rc+":"+dir.getLocalDir()+localname);
  607.       r.nocache();
  608.    }
  609.  }
  610.  } /* bad rc and good obj in cache */
  611.  // continue with "normal way"
  612.  r.send_headers(); 
  613.   
  614.  /* musime to ulozit na disk! */
  615.  File f=null;
  616.  switch(rc)
  617.  {
  618.         /* good RC */
  619.     case 200:        /* OK */
  620.     case 203:        /* Non-Authoritative Information */
  621.     case 300:        /* Multiple Choices */
  622.     case 301:               /* Moved Permanently */
  623.         case 302:        /* Moved Temporary */
  624.     case 307:        /* temporary redirect */
  625.     case 410:        /* Gone */
  626.     /* bad RC */
  627.     case 400:        /* Bad request */
  628.     case 404:        /* not found */
  629.     case 403:        /* Forbidden */
  630.     case 405:        /* method not allowed */
  631.                  f=genTmp();
  632.          break;
  633.  }
  634.  
  635.  if(!r.canCache()) f=null; /* non cacheable request */
  636.  DataOutputStream fout;
  637.  
  638.  try{
  639.  if(f!=null) fout=new DataOutputStream(
  640.                        new BufferedOutputStream(new FileOutputStream(f),4096));
  641.              else
  642.                fout=null;
  643.  }
  644.  catch (IOException e) {fout=null;}              
  645.  try{
  646.  r.transfer_object(sin,fout,dir);
  647.  }
  648.  catch (IOException e)
  649.   {
  650.    if(f!=null) f.delete();
  651.    System.out.println("FAILED: "+dir.getLocalDir()+localname+" ("+e+")");
  652.    throw e;
  653.   }
  654.  finally
  655.   {
  656.     b_miss+=r.getCsize();
  657.   }
  658.  sin.close();sou.close();toserver.close();
  659.  if(f==null) return;
  660.  /* stalled entry? - regenerate object name */
  661.  if(localname==null || localname.equals(RESERVED)) localname=genName(name);
  662.  
  663.  File n=new File(dir.getLocalDir()+localname);
  664.  
  665.  /* rename to new */
  666.  if(!rename(f,n)) { 
  667.   System.out.println("**REFRESH ERROR** Rename "+f+" to "+n+" failed.");
  668.   dir.dirty=true;return;}
  669.  /* update hlavicek */
  670.  clims=(lastmod>0?lastmod:date);
  671.  setInfo(r);
  672.  long delta;
  673.  if(lastmod>0) delta=(lastmod-clims)/60000L;
  674.    else
  675.                delta=(date-clims)/60000L;
  676.  
  677.  if(delta>60*48) System.out.println("Refreshed: "+n+" (delta="+delta/60/24+" d, size="+size+" B)");
  678.    else
  679.     if(delta>200) System.out.println("Refreshed: "+n+" (delta="+delta/60+" h, size="+size+" B)");                    
  680.       else
  681.         System.out.println("Refreshed: "+n+" (delta="+delta+" m, size="+size+" B)");
  682.  if(auto_compress) compress(9);
  683. }
  684.  
  685. final private synchronized boolean rename(File from, File to)
  686. {
  687.  to.delete();
  688.  boolean r=from.renameTo(to);
  689.  if(!r && !to.exists()) localname=null;
  690.  if(!r) from.delete();
  691.  return r;
  692. }
  693.  
  694. /*    S E N D    F R O M    C A C H E    */
  695.  
  696. final private void send_fromcache(request r) throws IOException
  697. {
  698.  dir.dirty=true;
  699.  if(r.getIms()>0)  { c_hit++;
  700.                      r.make_headers(304,null,null,null,0,0,0);
  701.                      r.send_headers();
  702.                      return;
  703.                     }
  704.  
  705.  /* otevrit soubor */
  706.  InputStream fin=null;
  707.  /* dekomprimovat */
  708.  boolean decomp=false;
  709.  if(encoding!=null && auto_decompress && ctype.startsWith("text/"))
  710.   {
  711.     if(r.getEncoding()==null) decomp=true;
  712.   }
  713.  try{
  714.  fin=
  715.     new BufferedInputStream(new FileInputStream(dir.getLocalDir()+localname),4096);
  716.     if(decomp) fin=new java.util.zip.GZIPInputStream(fin,4096);
  717.  }
  718.  catch (IOException e) { System.out.println("Missing: "+dir.getLocalDir()+localname);
  719.                          localname=null;
  720.                          load_object(r);
  721.                          return;}
  722.  c_hit++;
  723.  b_hit+=size;
  724.  if(generate_lastmod>0)
  725.  {
  726.     if(decomp) r.make_headers(httprc,ctype,null,location,0,lastmod==0? date:lastmod,expires);
  727.      else
  728.     r.make_headers(httprc,ctype,encoding,location,size,lastmod==0? date:lastmod,expires);
  729.  }
  730.    else
  731.  {
  732.     if(decomp)
  733.             r.make_headers(httprc,ctype,null,location,0,lastmod,expires);
  734.     else
  735.     r.make_headers(httprc,ctype,encoding,location,size,lastmod,expires);
  736.  }    
  737.  r.send_headers();
  738.  if(r.getMethod()==httpreq.REQUEST_GET) r.transfer_object(fin,null,dir);
  739. }
  740.  
  741. final private void setInfo(request r)
  742. {
  743.  httprc=r.getRc();
  744.  ctype=r.getCtype();
  745.  encoding=r.getEncoding();
  746.  location=r.getLocation();
  747.  size=r.getCsize();
  748.  lastmod=r.getIms();
  749.  expires=r.getExpires();
  750.  
  751.  date=System.currentTimeMillis();
  752.  /*
  753.  if(lastmod>=date) 
  754.    {
  755.    System.out.println("Warning Lastmod is in future! on "+dir.getLocalDir()+localname);
  756.    if(lastmod-date<1000*60*180) date=lastmod+1000l;
  757.      else
  758.       lastmod=0;
  759.    }
  760.  */
  761.  dir.dirty=true;
  762. }
  763.  
  764. final private boolean needRefresh(request r,long reload_age,long min_age, long max_age, float percent,long expire_age,long redir_age)
  765. {
  766.  // System.out.println("Checking "+dir.getLocalDir()+localname);
  767.  long now=System.currentTimeMillis();
  768.  long age=now-date;
  769.  /* - test reload status */
  770.  if(r.requestReload() && age>=reload_age) 
  771.    {
  772.     // System.out.println("Refresh: Refreshed on user request > RELOAD_AGE");
  773.     return true;
  774.    }
  775.    
  776.  if(age>max_age) 
  777.        {
  778.          // System.out.println("Refresh: AGE> MAXAGE "+age/60000+" "+max_age/60000);
  779.          return true; /* puvodne to bylo az za expires ... */
  780.        }
  781.  
  782.  
  783.  
  784.  if(expires!=0) 
  785.    {
  786.    if(expires<=now && age>=expire_age) 
  787.       {
  788.         // System.out.println("Expired...");
  789.         return true;
  790.       }
  791.     else
  792.      {
  793.      // System.out.println("Not Yet Expired...");
  794.      return false;
  795.      }
  796.    }
  797.  
  798.  /* redir rest */
  799.  if(location!=null)
  800.    if(age>=redir_age) return true;
  801.     else return false;
  802.      
  803.  /* min age test */
  804.  if(age<=min_age) 
  805.                  {
  806.                   // System.out.println("No Refresh: AGE<= MINAGE "+age/60000+" "+min_age/60000);
  807.                   return false;
  808.                   }
  809.           
  810.  if(lastmod==0) return true; /* usetrime si nejake to pocitani */
  811.  
  812.  /* lastmod check */
  813.  long lm_age=date - lastmod;
  814.  float lmfactor=(float) age / (float)lm_age;    
  815.  // System.out.println("lmfactor="+lmfactor+" percent="+percent);
  816.  
  817.  if(lmfactor<percent && lmfactor>0) return false;
  818.   else
  819.  return true; /* refresh it! */
  820. }
  821.  
  822. final public String toString()
  823. {
  824.  return dir.getLocalDir()+name;
  825. }
  826.  
  827. final public cachedir getDirectory()
  828. {
  829.  return dir;
  830. }
  831.  
  832. final public void setDirectory(cachedir newdir)
  833. {
  834.  if(dir==null) throw new IllegalArgumentException("Directory must not be null");
  835.  dir=newdir;
  836. }
  837.  
  838. final public long getLRU()
  839. {
  840.  return lru;
  841. }
  842.  
  843. final public int getRC()
  844. {
  845.  return httprc;
  846. }
  847.  
  848. final public int getSize()
  849. {
  850.  return size;
  851. }
  852.  
  853. final public long getDate()
  854. {
  855.  return date;
  856. }
  857.  
  858. final public long getExp()
  859. {
  860.  return expires;
  861. }
  862.  
  863. final public void delete()
  864. {
  865.  dir.remove(this);
  866.  if(localname==null) return;
  867.  // System.out.println("Deleted object: "+dir.getLocalDir()+localname);
  868.  new File(dir.getLocalDir()+localname).delete();
  869.  localname=null;
  870. }
  871.  
  872. final void touch()
  873. {
  874.                lru=date=System.currentTimeMillis();
  875.                dir.dirty=true;
  876. }
  877.  
  878. final void touchLRU()
  879. {
  880.                lru=System.currentTimeMillis();
  881.                dir.dirty=true;
  882. }
  883.  
  884. final public boolean isCheckable()
  885. {
  886.  if(lastmod==0) return false; else return true;
  887. }
  888. } /* class */
  889.