home *** CD-ROM | disk | FTP | other *** search
/ PC World 2001 April / PCWorld_2001-04_cd.bin / Software / TemaCD / smartcache / src / garbage.java < prev    next >
Text File  |  2001-01-26  |  33KB  |  1,042 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 garbage
  28. {
  29. public static final String GCVERSION="0.13";
  30.  
  31. public static final int EXPORT_LRU=1;
  32. public static final int EXPORT_DATE=2;
  33. public static final int EXPORT_FILEDATE=3;
  34. public static final int EXPORT_ALL=4;
  35.  
  36. public static boolean realdirsize;
  37.  
  38. private int cachesize;
  39. private int dirsize;
  40. private int oursize;
  41. private int lowmark,highmark;
  42. private int maxsize,refage;
  43. private int blocksize;
  44.  
  45. private cacheobject gcarray[];
  46. private float gcvalues[];
  47.  
  48. private int gcused;
  49. private float gclow;
  50.  
  51. private long now;
  52.  
  53. private int lopsize,lopsizeinc;
  54. private String lopact1,lopact2;
  55.  
  56. private int sopsize;
  57. private String sopact;
  58. private String exppenal, notchpenal,ebc;
  59. private String neg; /* negative cached */
  60. private regexp urlmask[];
  61. private String urlact[];
  62.  
  63. String root;
  64.  
  65. garbage(String root)
  66. {
  67.  this.root=root;
  68.  /* init to defaults */
  69.  cachesize=100*1024*1024; /* 100MB Cache */
  70.  maxsize=15*1024*1024;
  71.  lowmark=90;
  72.  highmark=95;
  73.  refage=365;
  74.  blocksize=4096;
  75.  dirsize=blocksize*2; 
  76.  lopsize=1024*1024*10;
  77.  gcused=0;
  78.  gclow=0;
  79.  gcarray=new cacheobject[1400];
  80.  gcvalues=new float[1400];
  81.  exppenal="*100";
  82.  ebc="*4";
  83.  neg="*10";
  84.  /* */
  85.  read_config(scache.GCFGFILE);
  86.  urlmask=fixup_urls(urlmask);
  87.  /* reverze it! */
  88.  urlmask=mgr.reverseRegexpArray(urlmask);
  89.  urlact=mgr.reverseStringArray(urlact);
  90.  now=System.currentTimeMillis();
  91.  System.out.println(new Date()+" Garbage collector version "+GCVERSION+" initialized.");
  92. }
  93.  
  94. private final void read_config(String cfgfile)
  95. {
  96.  try{
  97.  String line,token;
  98.  StringTokenizer st;
  99.  int lineno=0;
  100.  if(! new File(cfgfile).isAbsolute()) cfgfile=scache.cfgdir+File.separator+cfgfile;
  101.  
  102.  DataInputStream dis=new DataInputStream(new BufferedInputStream(new FileInputStream(cfgfile))); 
  103.  while ( (line = dis.readLine()) != null)
  104.  {
  105.           lineno++;
  106.           if(line.startsWith("#")) continue;
  107.       st=new StringTokenizer(line);
  108.           if(st.hasMoreTokens()==false) continue;
  109.           token=st.nextToken();
  110.           try
  111.           {
  112.           
  113.           if(token.equalsIgnoreCase("cache_size")) {cachesize=Integer.valueOf(st.nextToken()).intValue()*1024*1024;
  114.                                   continue;
  115.                                                     }
  116.  
  117.           if(token.equalsIgnoreCase("average_directory_size")) {dirsize=Integer.valueOf(st.nextToken()).intValue();
  118.                                   continue;
  119.                                                     }
  120.                                                     
  121.           if(token.equalsIgnoreCase("cache_low_mark")) {lowmark=Integer.valueOf(st.nextToken()).intValue();
  122.                                   continue;
  123.                                                     }
  124.  
  125.           if(token.equalsIgnoreCase("cache_high_mark")) {highmark=Integer.valueOf(st.nextToken()).intValue();
  126.                                   continue;
  127.                                                     }
  128.           if(token.equalsIgnoreCase("block_size")) {blocksize=Integer.valueOf(st.nextToken()).intValue();
  129.                                   continue;
  130.                                                     }
  131.                                                     
  132.           if(token.equalsIgnoreCase("gcarraysize")) {gcarray=new cacheobject[Integer.valueOf(st.nextToken()).intValue()];
  133.                                                      gcvalues=new float[gcarray.length];
  134.                                   continue;
  135.                                                     }
  136.                                                     
  137.           if(token.equalsIgnoreCase("maximum_object_size")) {
  138.                                                     maxsize=Integer.valueOf(st.nextToken()).intValue()*1024;
  139.                                                     if(maxsize<=0) maxsize=Integer.MAX_VALUE;
  140.                                   continue;
  141.                                                     }
  142.           if(token.equalsIgnoreCase("reference_age")) {
  143.                                                     refage=Integer.valueOf(st.nextToken()).intValue();
  144.  
  145.                                                     if(refage<=0) refage=Integer.MAX_VALUE;
  146.                                   continue;
  147.                                                     }
  148.  
  149.           if(token.equalsIgnoreCase("large_object_penalty")) {
  150.                                                     lopsize=Integer.valueOf(st.nextToken()).intValue();
  151.                                                     lopact1=st.nextToken();
  152.                                                     lopsizeinc=Integer.valueOf(st.nextToken()).intValue();
  153.                                                     lopact2=st.nextToken();
  154.                                   continue;
  155.                                                     }
  156.           if(token.equalsIgnoreCase("small_object_penalty")) {
  157.                                                     sopsize=Integer.valueOf(st.nextToken()).intValue();
  158.                                                     sopact=st.nextToken();
  159.                                   continue;
  160.                                                     }
  161.                                                     
  162.           if(token.equalsIgnoreCase("expired_penalty")) {
  163.                                                     exppenal=st.nextToken();
  164.                                   continue;
  165.                                                     }
  166.                                                     
  167.           if(token.equalsIgnoreCase("expired_but_checkable_penalty")) {
  168.                                                     ebc=st.nextToken();
  169.                                   continue;
  170.                                                     }
  171.  
  172.           if(token.equalsIgnoreCase("not_checkable_penalty")) {
  173.                                                     notchpenal=st.nextToken();
  174.                                   continue;
  175.                                                     }
  176.                                                     
  177.           if(token.equalsIgnoreCase("negative_cached_penalty")) {
  178.                                                     neg=st.nextToken();
  179.                                   continue;
  180.                                                     }
  181.           if(token.equals("case_sensitive_matching")) {
  182.                                              char c=(char)Integer.valueOf(st.nextToken()).intValue();
  183.                                                            if(c==1) mgr.case_sensitive=true; else mgr.case_sensitive=false;
  184.                                                            continue;
  185.                                                       }
  186.                                                     
  187.           if(token.equalsIgnoreCase("urlmask")) {
  188.                                token=st.nextToken();
  189.                                                  if(mgr.isInRegexpArray(token,urlmask)) continue;
  190.                                                  urlmask=mgr.addRegexpToArray(token,urlmask);
  191.                                                  urlact=mgr.addStringToArray(st.nextToken(),urlact);
  192.                                continue;
  193.                                                  }
  194.           }
  195.           catch (NoSuchElementException z)
  196.            {
  197.           System.out.println("[GC_CONFIG_ERROR] Missing argument(s) in "+cfgfile+":"+lineno);
  198.           continue;
  199.            }
  200.            
  201.           catch (NumberFormatException z)
  202.            {
  203.           System.out.println("[GC_CONFIG_ERROR] Invalid number in "+cfgfile+":"+lineno);
  204.           continue;
  205.            }
  206.  
  207.           System.out.println("[GC_CONFIG_ERROR] Unknown keyword "+token+" in "+cfgfile+":"+lineno);
  208.  }/* while */
  209.  dis.close();
  210.  if(dirsize< 2*blocksize) dirsize=2*blocksize;
  211.  if(dirsize<4096) dirsize=4096;
  212.  }
  213.  catch(IOException e) {}
  214.  
  215. }
  216.  
  217. private final regexp[] fixup_urls(regexp what[])
  218. {
  219.  if(what==null) return null;
  220.  for(int i=0;i<what.length;i++)
  221.   {
  222.   regexp r;
  223.   r=new regexp( encodechars(what[i].toString()),false);
  224.   what[i]=r;
  225.   }
  226.   return what;
  227. }
  228.  
  229. public final void rebalance()
  230. {
  231.  if(cachedir.readonly==true)
  232.  {
  233.      System.out.println(new Date()+" Cache directory is in READ-ONLY mode, operation Aborted.");
  234.      return;
  235.  }
  236.  rebalance0(root);
  237. }
  238.  
  239. public final void dirimport(String improot)
  240. {
  241.  import0(improot,improot);
  242. }
  243.  
  244. public final void export(String to,int type,long difftime)
  245. {
  246.  export_one_dir(root,to,type,difftime);
  247. }
  248.  
  249. public final void cacheimport(String improot)
  250. {
  251.  importcache0(improot);
  252. }
  253.  
  254. public final void cleanup()
  255. {
  256.  if(cachedir.readonly==true)
  257.  {
  258.      System.out.println(new Date()+" Cache directory is in READ-ONLY mode, operation Aborted.");
  259.      return;
  260.  }
  261.  kill_unref_dir(root,true);
  262. }
  263.  
  264. public final void converto020()
  265. {
  266.  convert020dir(root);
  267. }
  268.  
  269. public final void converto030()
  270. {
  271.  convert030dir(root);
  272. }
  273.  
  274. private final void dump_gcarray()
  275. {
  276.  System.out.println("\n"+new Date()+" Dumping garbage collection memory array:\nComputed LRU value in days\t\tURL\n");
  277.  for(int i=gcused;i>=0;i--)
  278.   {
  279.    if(gcarray[i]==null) continue;
  280.    System.out.println(gcvalues[i]+"  "+gcarray[i].getDirectory().getLocalDir()+gcarray[i].getName());
  281.   }
  282.  System.out.println("\n"+new Date()+"   ... End of dump.");
  283. }
  284.  
  285. private final void convert020dir(String dirname)
  286. {
  287.   File root=new File(dirname);
  288.   
  289.   String dirfilez[];
  290.   Vector filez=new Vector();
  291.   
  292.   if(!root.isDirectory()) return;
  293.   
  294. //  if(!new File(dirname,cachedir.DIRINFO).exists()) return;
  295.   try{
  296.   
  297.   DataInputStream dis=new DataInputStream( new BufferedInputStream(new FileInputStream(root+File.separator+cachedir.DIRINFO)));
  298.   
  299.   DataOutputStream dos=new DataOutputStream( new BufferedOutputStream(new FileOutputStream(root+File.separator+cachedir.DIRINFO+"new")));
  300.   
  301.   int howmany=dis.readInt();
  302.   dos.writeInt(howmany);
  303.   
  304.   for(int i=0;i<howmany;i++)
  305.   {
  306.   String s;
  307.   /* name */
  308.   s=dis.readUTF();
  309.   dos.writeUTF(s);
  310.   /* ctype */  
  311.   s=dis.readUTF();
  312.   dos.writeUTF(s);
  313.   /* localname */  
  314.   s=dis.readUTF();
  315.   dos.writeUTF(s);
  316.   /* location */
  317.   s=dis.readUTF();
  318.   dos.writeUTF(s);
  319.   int x;
  320.   /* size */
  321.   x=dis.readInt();
  322.   dos.writeInt(x);
  323.   /* httprc */
  324.   x=dis.readInt();
  325.   dos.writeInt(x);
  326.   long l;
  327.   /* expires */
  328.   l=dis.readLong();
  329.   dos.writeLong(l);
  330.   /* date */
  331.   l=dis.readLong();
  332.   dos.writeLong(l);
  333.   /* lastmod */
  334.   l=dis.readLong();
  335.   dos.writeLong(l);
  336.   /* LRU */
  337.   dos.writeLong(System.currentTimeMillis());
  338.       
  339.   }
  340.  dis.close(); 
  341.  dos.close();  
  342.  new File(dirname,cachedir.DIRINFO).delete();
  343.  if(!new File(dirname,cachedir.DIRINFO+"new").renameTo(new File(dirname,cachedir.DIRINFO)))
  344.    System.out.println("Rename failed in dir "+dirname);
  345.  }
  346.   catch (FileNotFoundException e1) {}
  347.   catch (Exception e) {System.out.println(e);}
  348.   
  349.   dirfilez=root.list();
  350.   /* rekurzivne do adresaru */
  351.   for(int i=0;i<dirfilez.length;i++)
  352.    {
  353.      if(!new File(root,dirfilez[i]).isDirectory()) continue;
  354.      convert020dir(dirname+File.separator+(String)dirfilez[i]);
  355.    }  
  356. }
  357.  
  358. private final void import0(String improot,String dirname)
  359. {
  360.  File myroot=new File(dirname);
  361.  if(!myroot.isDirectory()) return;
  362.  
  363.  if(dirname.length()>improot.length())
  364.  {
  365.    String urlpart;
  366.    String hp;
  367.    urlpart=dirname.substring(improot.length()+1);
  368.    // roseknuti na host cast adresare a URL cast
  369.    if(urlpart.indexOf(File.separator)==-1)
  370.    {
  371.      hp=urlpart;
  372.      urlpart="";
  373.    }
  374.    else
  375.    {
  376.      hp=urlpart.substring(0,urlpart.indexOf(File.separator));
  377.      urlpart=urlpart.substring(hp.length());
  378.    }
  379.    // extract hostname from filename
  380.    String hn;
  381.    hn=hp.toLowerCase();
  382.    if(hn.indexOf('_')>-1)
  383.       hn=hn.substring(0,hn.indexOf('_'));
  384.    if(hn.indexOf('^')>-1)
  385.       hn=hn.substring(0,hn.indexOf('^'));
  386.       
  387.    if(hn.indexOf('.')==-1)
  388.     {
  389.       System.out.println(new Date()+" [IMPORT] ignoring host without dot in name: "+hn);
  390.       return;
  391.      }
  392.       // spocitat crc32 hash
  393.       StringBuffer result;
  394.       result=new StringBuffer(80);
  395.       result.append(root);
  396.       int ii;
  397.  
  398.       /* 1. spocitat hash z host stringu */
  399.       java.util.zip.CRC32 crc=new CRC32();
  400.       crc.update(hn.getBytes());
  401.       /* mame hash - rozdelime ho na adresar */
  402.       long unit=((long)1<<32)/(mgr.swap_level1_dirs*mgr.swap_level2_dirs);
  403.       ii=(int)(crc.getValue()/unit);
  404.       /* zacneme budovat cestu */
  405.       result.append(File.separator+(ii/mgr.swap_level2_dirs)+File.separator+(ii-mgr.swap_level1_dirs*(ii/mgr.swap_level2_dirs))+File.separator+hp+urlpart+File.separator);
  406.  
  407.    cachedir imp,my;
  408.    /* create .cacheinfo in to-be-imported directory */
  409.    imp=repair.repairDir(dirname,false);
  410.    my=new cachedir(result.toString());
  411.    // System.out.println("hp="+hp+"("+hn+") urlpart="+urlpart+" mydir="+result);
  412.    my.merge(imp);
  413.    my.save();
  414.  }
  415.  
  416.  // into subdirz
  417.  String filez[];
  418.  filez=myroot.list();
  419.  if(filez==null) return;
  420.  for(int i=filez.length-1;i>=0;i--)
  421.   if(new File(dirname+File.separator+filez[i]).isDirectory()) import0(improot,dirname+File.separator+filez[i]);
  422.  
  423. }
  424.  
  425. private final void rebalance0(String dirname)
  426. {
  427.  File myroot=new File(dirname);
  428.  boolean goodlevel=false;
  429.  
  430.  if(!myroot.isDirectory()) return;
  431.  
  432.  /* zjistit, zda jsme jiz na spravne urovni t.j. */
  433.  /* root=d:\store
  434.     my           \x\y */
  435.  
  436.  if(dirname.length()-this.root.length()>3)
  437.  {
  438.   // je uz to mozne.... ?
  439.   String nd=dirname.substring(this.root.length()+1);
  440.   int i=nd.indexOf(File.separatorChar,0);
  441.   if(i!=-1) {
  442.      // System.out.println("[debug] Good level="+dirname);
  443.      goodlevel=true;
  444.             }
  445.  }
  446.   /* rekurzivne do adresaru */
  447.   String dirfilez[]=myroot.list();
  448.   for(int i=0;i<dirfilez.length;i++)
  449.    {
  450.      if(!new File(myroot,dirfilez[i]).isDirectory()) continue;
  451.      if(goodlevel==true)
  452.      {
  453.       // extract hostname from filename
  454.       String hn;
  455.       hn=dirfilez[i];
  456.       if(hn.indexOf('_')>-1)
  457.         hn=hn.substring(0,hn.indexOf('_'));
  458.     
  459.       if(hn.indexOf('^')>-1)
  460.         hn=hn.substring(0,hn.indexOf('^'));
  461.       hn=hn.toLowerCase();
  462.       // System.out.println("fn="+dirname+File.separator+dirfilez[i]+" hn="+hn);
  463.  
  464.       // spocitat crc32 hash
  465.       StringBuffer result=new StringBuffer(90);
  466.       result.append(root);
  467.  
  468.       /* 1. spocitat hash z host stringu */
  469.       java.util.zip.CRC32 crc=new CRC32();
  470.       crc.update(hn.getBytes());
  471.       /* mame hash - rozdelime ho na adresar */
  472.       int ii;
  473.       ii=(int)(crc.getValue()/(0x100000000L/(mgr.swap_level1_dirs*mgr.swap_level2_dirs)));
  474.       result.append(File.separator+(ii/mgr.swap_level2_dirs)+File.separator+(ii % mgr.swap_level2_dirs)+File.separator+dirfilez[i]);
  475.       // System.out.println("crc="+crc.getValue()+" units="+ii+" np="+result);
  476.       if(!result.toString().equals(dirname+File.separator+dirfilez[i]))
  477.        {
  478.           File f1,f2;
  479.       f1=new File(myroot,dirfilez[i]); // old
  480.       f2=new File(result.toString());
  481.           new File(f2.getParent()).mkdirs();
  482.       if(!f1.renameTo(f2))
  483.         System.out.println("Rename "+f1+" to "+f2+" failed.");
  484.  
  485.        }
  486.  
  487.      } else /* goodleve==false */
  488.          rebalance0(dirname+File.separator+(String)dirfilez[i]);
  489.    }  
  490.    myroot.delete();
  491. }
  492.  
  493. private final void convert030dir(String dirname)
  494. {
  495.   File root=new File(dirname);
  496.   
  497.   String dirfilez[];
  498.   Vector filez=new Vector();
  499.   boolean goodlevel=false;
  500.   if(!root.isDirectory()) return;
  501.   
  502.   /* zjistit,  zda jsme na spravnem levelu t.j. d:\store\x\y\zzzzz.yy */
  503.   if(dirname.length()-this.root.length()>5)
  504.   {
  505.   String nd=dirname.substring(this.root.length()+1);
  506.   int i=nd.indexOf(File.separatorChar,0);
  507.   if(i!=-1) {
  508.   nd=nd.substring(i+1);
  509.   i=i=nd.indexOf(File.separatorChar,0);
  510.   if(i!=-1) goodlevel=true;
  511.   }
  512.   // System.out.println(dirname+" = "+nd);
  513.   
  514.   }
  515.   
  516.   /* rekurzivne do adresaru */
  517.   dirfilez=root.list();
  518.   for(int i=0;i<dirfilez.length;i++)
  519.    {
  520.      if(!new File(root,dirfilez[i]).isDirectory()) continue;
  521.      if(goodlevel==true && dirfilez[i].startsWith("-"))
  522.      {
  523.       new File(root,dirfilez[i]).renameTo(new 
  524.        File(root,"~"+dirfilez[i].substring(1))  );
  525.      }
  526.      if(goodlevel==false) convert030dir(dirname+File.separator+(String)dirfilez[i]);
  527.    }  
  528. }
  529.  
  530.  
  531. /* ************************************* */
  532. /* ALERT: Synchronize by hand with mgr!! */
  533. /* ************************************* */
  534.  
  535. private final String encodechars(String urldir)
  536. {
  537.  /* odstranit http:// if any */
  538.   // System.out.println("Encode in="+urldir);
  539.   /* get protocol if any */
  540.   int i=urldir.indexOf("://",0);
  541.   if(i!=-1)
  542.    {
  543.     String proto=urldir.substring(0,i);
  544.     urldir=urldir.substring(i+3); //cut of protocol
  545.     if(!proto.equalsIgnoreCase("http"))
  546.      {
  547.       /* ftp://ftp.debian.org/* -> ftp.debian.org^ftp/* */
  548.       /* ftp://ftp.lamer* -> ftp.lamer*^ftp             */
  549.       
  550.       i=urldir.indexOf('/',0);
  551.       if(i==-1)  urldir+='^'+proto+'*';
  552.         else
  553.       urldir=urldir.substring(0,i)+'^'+proto+urldir.substring(i);
  554.      }
  555.    }
  556.   // if(urldir.startsWith("http://")) urldir=urldir.substring(7);
  557.  
  558.   /* nahrazujeme nepratelske znaky */
  559.   urldir=urldir.replace(':','_');
  560.   // urldir=urldir.replace('*','@');
  561.   // urldir=urldir.replace('?','#');  
  562.   // urldir=urldir.replace('~','-');
  563.   // urldir=urldir.replace('>','}');  
  564.   // urldir=urldir.replace('<','{');
  565.   urldir=urldir.replace('|','!');
  566.   if(File.separatorChar!='/') urldir=urldir.replace('/',File.separatorChar);
  567.   // System.out.println("encode debug="+urldir);
  568.   return urldir;
  569. }
  570.  
  571. public final void garbage_collection()
  572. {
  573.  int bot=(int)( (float)cachesize*(float)lowmark/100f);
  574.  int high=(int)( (float)cachesize*(float)highmark/100f);
  575.  if(cachedir.readonly==true)
  576.  {
  577.      System.out.println(new Date()+" Cache directory is in READ-ONLY mode, GC Aborted.\n"+new Date()+"\tYou can try use -fakegc instead");
  578.      return;
  579.  }
  580.  
  581.  System.out.println(new Date()+" Cache size watermarks: Low="+bot+" ("+lowmark+"%) High="+high+" ("+highmark+"%)");
  582.  // boolean cleaning=false;
  583.  oursize=high+1;
  584.  while(oursize>high)
  585.  {
  586.  oursize=0;
  587.  gcarray=new cacheobject[gcarray.length];
  588.  gcvalues=new float[gcvalues.length];
  589.  System.out.println(new Date()+" Scanning cache directory. This may take some time...");
  590.  scan_cache(root);
  591.  System.out.println(new Date()+" Scan end. Cachesize="+oursize+
  592.  " B\n"+new Date()+" GC array time marks: Low ="+gclow+" high="+
  593.  (gcused>=gcvalues.length ? new Float(gcvalues[gcvalues.length])
  594.  : new Float(gcvalues[gcused])));
  595.  // dump_gcarray();
  596.  delete_files();
  597.  // if(cleaning==false && oursize<high) break; else cleaning=true; 
  598.  }
  599.  
  600. public final void fake_garbage_collection()
  601. {
  602.  refage=maxsize=Integer.MAX_VALUE;
  603.  int bot=(int)( (float)cachesize*(float)lowmark/100f);
  604.  int high=(int)( (float)cachesize*(float)highmark/100f);
  605.  System.out.println(new Date()+" Cache size watermarks: Low="+bot+" ("+lowmark+"%) High="+high+" ("+highmark+"%)"); 
  606.  // boolean cleaning=false;
  607.  oursize=0;
  608.  gcarray=new cacheobject[gcarray.length];
  609.  gcvalues=new float[gcvalues.length];
  610.  System.out.println(new Date()+" Scanning cache directory. This may take some time...");
  611.  scan_cache(root);
  612.  System.out.println(new Date()+" Scan end. Cachesize="+oursize+
  613.  " B\n"+new Date()+" GC array time marks: Low ="+gclow+" high="+
  614.  (gcused>=gcvalues.length ? new Float(gcvalues[gcvalues.length])
  615.  : new Float(gcvalues[gcused])));
  616.  dump_gcarray();
  617.  fake_delete_files();
  618.  // if(cleaning==false && oursize<high) break; else cleaning=true; 
  619.  
  620. private final void delete_files()
  621. {
  622.  int bot=(int)( (float)cachesize*(float)lowmark/100f);
  623.  int high=(int)( (float)cachesize*(float)highmark/100f);
  624.  if(oursize<bot)
  625.   {
  626.    System.out.println(new Date()+" Cache size is OK - No further cleaning needed");
  627.    return;
  628.   }
  629.  
  630.  System.out.println(new Date()+" Cleaning files....");
  631.  for(int i=gcused;i>=0;i--)
  632.  {
  633.   if(gcarray[i]==null) continue;
  634.   /* smazat soubor */
  635.   /* TODO: vytisknout nazev souboru! */
  636.   System.out.println("Deleted: "+gcarray[i].getDirectory().getLocalDir()+File.separator+gcarray[i].getLocalName());
  637.   gcarray[i].delete();
  638.   int size;
  639.   size=gcarray[i].getSize();
  640.   oursize-= (size/blocksize)*blocksize;
  641.   if(size % blocksize !=0 ) oursize-=blocksize;
  642.   
  643.   if(gcarray[i].getDirectory().countObjects()==0)
  644.    {
  645.     /* TODO half dir! */
  646.     oursize-=dirsize;
  647.     gcarray[i].getDirectory().cleandir();
  648.    }
  649.    if(oursize<bot) break;
  650.  }
  651.  /* SAVE Directories */
  652.  System.out.println(new Date()+" Updating directory information...");
  653.  for(int i=gcused;i>=0;i--)
  654.  {
  655.   if(gcarray[i]==null) continue;
  656.   gcarray[i].getDirectory().save();
  657.   
  658.  } 
  659.  System.out.println(new Date()+" Done. Cachesize is now "+oursize+" bytes.");
  660.  if(oursize>high) System.out.println(new Date()+" WARNING: Another rescan cache needed to finish GC.\nIf this happens frequently increase gcarraysize in gc.cnf.\ngcarraysize is now set to "+gcarray.length+", good practice is add 20% to it.");
  661. }
  662.  
  663. private final void fake_delete_files()
  664. {
  665.  int bot=(int)( (float)cachesize*(float)lowmark/100f);
  666.  int high=(int)( (float)cachesize*(float)highmark/100f);
  667.  if(oursize<bot)
  668.   {
  669.    System.out.println(new Date()+" [FAKE] Cache size is OK - No further cleaning needed");
  670.    return;
  671.   }
  672.  
  673.  System.out.println(new Date()+" [FAKE] Not Cleaning files....");
  674.  for(int i=gcused;i>=0;i--)
  675.  {
  676.   if(gcarray[i]==null) continue;
  677.   /* smazat soubor */
  678.   System.out.println("[FAKE] Delete "+gcarray[i].getDirectory()+gcarray[i].getName());
  679.   int size;
  680.   size=gcarray[i].getSize();
  681.   oursize-= (size/blocksize)*blocksize;
  682.   if(size % blocksize !=0 ) oursize-=blocksize;
  683.   
  684.   if(oursize<bot) break;
  685.  }
  686.  
  687.  System.out.println(new Date()+" [FAKE] Done. Cachesize is now "+oursize+" bytes.");
  688.  if(oursize>high) System.out.println(new Date()+" [FAKE] WARNING: Another rescan cache needed to finish GC.");
  689. }
  690.  
  691. /* proscanuje rekurzivne cache a naplni gcarray */
  692. private final void scan_cache(String dirname)
  693. {
  694.  oursize+=dirsize;
  695.  cachedir cd;
  696.  /* nacist adresar */
  697.  cd=kill_unref_dir(dirname,false);
  698.  /* nacpat do gc pole */
  699.  if(cd!=null)
  700.   {
  701.    /* prevest localname na URL */
  702.    /* ze d:\store\XX\YY\name */
  703.    /* udelat name */
  704.    String urlpart;
  705.    urlpart=cd.getLocalDir().substring(root.length()+1);
  706.    /* preskocit 2x file-separator */
  707.    int i;
  708.    i=urlpart.indexOf(File.separatorChar,0);
  709.    if(i!=-1) {
  710.       urlpart=urlpart.substring(i+1); 
  711.       i=urlpart.indexOf(File.separatorChar,0);
  712.       if(i!=-1) urlpart=urlpart.substring(i+1); 
  713.                 else 
  714.              { System.out.println(new Date()+" Scanning "+dirname); urlpart="";}
  715.       } else { urlpart="";}
  716.    
  717.    // System.out.println(cd.getLocalDir()+"="+urlpart);
  718.    
  719.    Enumeration en;
  720.    en=cd.getObjects();
  721.    while(en.hasMoreElements())
  722.    {
  723.    cacheobject obj;
  724.    obj=(cacheobject)en.nextElement();
  725.    /* spocitat vahu objektu */
  726.    float w;
  727.    w=(now-obj.getLRU())/86400000.0000f;
  728.    /* large obj penalty */
  729.    int size;
  730.    size=obj.getSize();
  731.    if(size>maxsize)
  732.                     {  
  733.               System.out.println("Deleted too large object: "+dirname+File.separator+obj.getLocalName()+" size="+w+"B");
  734.             obj.delete();continue; }
  735.    oursize+= (size/blocksize)*blocksize;
  736.    if(size % blocksize !=0 ) oursize+=blocksize;
  737.    
  738.    if(size>lopsize) { size-=lopsize;w=applyMod(w,lopact1);
  739.                       while(size>lopsizeinc)
  740.                        {
  741.                         size-=lopsizeinc;
  742.                         w=applyMod(w,lopact2);
  743.                        }
  744.                     } /* LOP HACK */
  745.   else
  746.    if(size<=sopsize) w=applyMod(w,sopact);
  747.    
  748.   if(obj.getRC()>=400) w=applyMod(w,neg);
  749.   long xsize;
  750.   xsize=obj.getExp();
  751.   boolean chk;
  752.   chk=obj.isCheckable();
  753.   if(xsize>0 && xsize<now) 
  754.    if(chk) w=applyMod(w,ebc); else
  755.            w=applyMod(w,exppenal);
  756.    else
  757.     if (chk==false) w=applyMod(w,notchpenal);
  758.   
  759.   // System.out.println(dirname+obj.getName()+" w="+w);
  760.   
  761.   /* URL masky */
  762.   if(urlmask!=null)
  763.   {
  764.   for(size=urlmask.length-1;size>=0;size--)
  765.    if(urlmask[size].matches(urlpart+obj.getName())) { w=applyMod(w,urlact[size]);break;}
  766.   }
  767.   /* refage delete */ 
  768.   if(w>=refage) {
  769.                 size=obj.getSize();
  770.                 oursize-= (size/blocksize)*blocksize;
  771.                 if(size % blocksize !=0 ) oursize-=blocksize;
  772.         System.out.println("Deleted too old object: "+dirname+File.separator+obj.getLocalName()+" age="+w+", max="+refage);
  773.                 obj.delete();
  774.         continue; }
  775.   
  776.   /* pridat do gc pole */
  777.   if(w<gclow) continue;
  778.   /* najit misto pro vlozeni pomoci puleni intervalu */
  779.   int l,h;
  780.   l=0;
  781.   h=gcused;
  782.   while (l!=h)
  783.   {
  784.     int z;
  785.     z=(l+h)/2;
  786.     if(w<gcvalues[z]) {h=z;continue;}
  787.     if(w>gcvalues[z]) {l=z+1;continue;}
  788.     l=z;break;
  789.   }
  790.   /* pridat za pozici l */
  791.   if(w<gcvalues[l]) l--;
  792.   
  793.   /* pridat doprava */
  794.      if(gcused<gcvalues.length-1)
  795.       {
  796.        /* jeste mame misto - posunuje se doprava*/
  797.        // System.out.println("right l="+l+" gcused="+gcused);
  798.        System.arraycopy(gcvalues,l+1,gcvalues,l+2,gcvalues.length-l-2);
  799.        System.arraycopy(gcarray,l+1,gcarray,l+2,gcarray.length-l-2);
  800.        gcvalues[l+1]=w;
  801.        gcarray[l+1]=obj;
  802.        gcused++;
  803.       }
  804.      else
  805.       {
  806.        /* misto neni - jedeme do leva */
  807.        // System.out.println("left l="+l+" gcused="+gcused);
  808.        if(l>0)
  809.        {
  810.         System.arraycopy(gcvalues,1,gcvalues,0,l);
  811.         System.arraycopy(gcarray,1,gcarray,0,l);
  812.        }
  813.        gcvalues[l]=w;
  814.        gcarray[l]=obj;
  815.        gclow=gcvalues[0];
  816.       }
  817.     
  818.   } /* elements */
  819.   /* TODO - half empty directory! */
  820.   if(cd.countObjects()==0) { cd.cleandir();oursize-=dirsize;} else
  821.   cd.save();
  822.   } /* cd not null */
  823.   
  824.   /* dive into subdirz */
  825.   /* potrebuje mnoho pameti!!! */
  826.   File root=new File(dirname);
  827.   
  828.   String filez[];
  829.   filez=root.list();
  830.   if(filez==null) { oursize-=dirsize;return;}
  831.   for(int i=filez.length-1;i>=0;i--)
  832.    if(new File(dirname+File.separator+filez[i]).isDirectory()) scan_cache(dirname+File.separator+filez[i]);
  833. }
  834.  
  835. private final float applyMod(float f,String mod)
  836. {
  837.  if(mod==null) return f;
  838.  float m;
  839.  try
  840.  {
  841.   m=Float.valueOf(mod.substring(1)).floatValue();
  842.  }
  843.  catch ( NumberFormatException z)
  844.   {
  845.    System.out.println("[GC_CONFIG_ERROR] Invalid number in LRU modifier '"+mod+"'");
  846.    throw z;
  847.   }
  848.  switch(mod.charAt(0))
  849.  {
  850.   case '+':f+=m;break;
  851.   case '-':f-=m;break;
  852.   case '*':f*=m;break;
  853.   case '/':f/=m;break;
  854.   case '=':if(f>=m) f=refage;
  855.            break;
  856.   default:
  857.    System.out.println("[GC_CONFIG_ERROR] Invalid action in LRU modifier '"+mod+"'");  
  858.    throw new IllegalArgumentException("Unknown action "+mod.charAt(0));
  859.  }
  860.  return f;
  861. }
  862.  
  863. private final cachedir kill_unref_dir(String dirname, boolean recurse)
  864. {
  865.   File root=new File(dirname);
  866.   
  867.   String dirfilez[];
  868.   String local[];
  869.   Vector filez=new Vector();
  870.   
  871.   if(!root.isDirectory()) return null;
  872.   dirfilez=root.list();
  873.   if(dirfilez==null) dirfilez=new String[0];
  874.   cachedir cd=new cachedir(dirname+File.separator);
  875.   cd.checkDir();
  876.   local=cd.listLocalNames();
  877.   cd.save();
  878.   /* konverze dirfilez na vector */
  879.   for(int i=dirfilez.length-1;i>=0;i--)
  880.    filez.addElement(dirfilez[i]);
  881.   dirfilez=null;
  882.  
  883.   /* smazat localnames z filez, abychom je nesmazali */
  884.   for(int i=local.length-1;i>=0;i--)
  885.    filez.removeElement(local[i]);
  886.    
  887.   /* abychom si nesmazali cacheinfo !! */
  888.   /* pokud jsme empty dir, tak ho smazeme */
  889.      
  890.   if(cd.countObjects()>0 ) filez.removeElement(cachedir.DIRINFO);
  891.   if(recurse==true) cd=null;
  892.     
  893.   local=null;
  894.   long now=System.currentTimeMillis();
  895.   /* projedeme filez a smazeme nalezene soubory */
  896.   for(int i=filez.size()-1;i>=0;i--)
  897.    {
  898.     File df=null;
  899.     try{
  900.     df=new File(dirname+File.separator+(String)filez.elementAt(i));
  901.     }
  902.     catch (ArrayIndexOutOfBoundsException z) {continue;}
  903.     if(!df.isDirectory())
  904.       {
  905.        // .tmp age check
  906.        if(now-df.lastModified()>1000*6*60)
  907.         {
  908.          System.out.println("Deleting orphan file: "+df);
  909.          df.delete();
  910.     }
  911.        filez.removeElementAt(i);       
  912.        i++;continue;
  913.       }
  914.    }
  915.   if(!recurse) { root.delete();return cd;}  
  916.   filez.trimToSize();
  917.   
  918.   /* rekurzivne do adresaru */
  919.   for(int i=filez.size()-1;i>=0;i--)
  920.    {
  921.      kill_unref_dir(dirname+File.separator+(String)filez.elementAt(i),true);
  922.    }  
  923.   root.delete(); 
  924.   return cd;
  925. }
  926.  
  927. private final void importcache0(String dirname)
  928. {
  929.  File myroot=new File(dirname);
  930.  if(!myroot.isDirectory()) return;
  931.  
  932.   /* rekurzivne do adresaru */
  933.   String dirfilez[]=myroot.list();
  934.   for(int i=0;i<dirfilez.length;i++)
  935.    {
  936.      if(!new File(myroot,dirfilez[i]).isDirectory()) continue;
  937.      try { 
  938.           Integer.valueOf(dirfilez[i]);
  939.      }
  940.      catch (Exception exx) { continue;}
  941.      String subdirz[];
  942.      subdirz=new File(dirname,dirfilez[i]).list();
  943.      for(int j=0;j<subdirz.length;j++)
  944.      {
  945.           if(!new File(myroot+File.separator+dirfilez[i],subdirz[j]).isDirectory()) continue;
  946.           try { 
  947.               Integer.valueOf(subdirz[j]);
  948.           }
  949.           catch (Exception exx) { continue;}
  950.           import0(dirname+File.separator+dirfilez[i]+File.separator+subdirz[j],
  951.               dirname+File.separator+dirfilez[i]+File.separator+subdirz[j]);
  952.       }
  953.    }
  954. } /* import cache 0 */
  955.  
  956. /* export */
  957. private final void  export_one_dir(String dirname, String to, int type, long difftime)
  958. {
  959.   File root=new File(dirname);
  960.   
  961.   if(!root.isDirectory()) return;
  962.   cachedir cd=new cachedir(dirname+File.separator);
  963.   boolean export=false;
  964.   if(type!=EXPORT_ALL)
  965.   {
  966.     long now=System.currentTimeMillis();
  967.     Enumeration en=cd.getObjects();
  968.     while(en.hasMoreElements())
  969.     {
  970.      cacheobject obj;
  971.      obj=(cacheobject)en.nextElement();
  972.      if(type==EXPORT_LRU)
  973.         if (now-obj.getLRU()<=difftime) { export=true;break;}
  974.     else continue;
  975.      if(now-obj.getDate()<=difftime) { export=true;break;}
  976.     }
  977.  } else export=true;
  978.  
  979.  if(export)
  980.   {
  981.    String en;
  982.    en=dirname.substring(this.root.length()+1);
  983.    en=en.substring(en.indexOf(File.separator)+1);
  984.    en=en.substring(en.indexOf(File.separator)+1);
  985.    // System.out.println("Exporting: "+dirname+" ="+en);
  986.    cachedir ncd;
  987.    ncd=new cachedir(to+File.separator+en+File.separator);
  988.    cd.export_to(ncd,type,difftime);
  989.    ncd.save();
  990.    ncd.cleandir();
  991.   }
  992.  
  993.  // into subdirz
  994.  String filez[];
  995.  filez=root.list();
  996.  if(filez==null) return;
  997.  for(int i=filez.length-1;i>=0;i--)
  998.   if(new File(dirname+File.separator+filez[i]).isDirectory()) export_one_dir(dirname+File.separator+filez[i],to,type,difftime);
  999. }
  1000.  
  1001. public final static long timestring(String s)
  1002. {
  1003.  long multi=60*1000L; // minutes
  1004.  long value=0;
  1005.  if(s==null) return 0;
  1006.  if(s.length()>1)
  1007.   {
  1008.    char c=s.charAt(s.length()-1);
  1009.    switch(c)
  1010.    {
  1011.     case 'd':
  1012.     case 'D':
  1013.               multi=24*60*60*1000L;
  1014.           break;
  1015.     case 'W':
  1016.     case 'w':
  1017.               multi=7*24*60*60*1000L;
  1018.           break;
  1019.     case 'm':
  1020.               multi=60*1000L; // minutes
  1021.               break;
  1022.     case 'M':
  1023.               multi=30*24*60*60*1000L;
  1024.           break;
  1025.     case 'y':
  1026.     case 'Y':
  1027.               multi=365*24*60*60*1000L;
  1028.           break;
  1029.     case 'h':
  1030.     case 'H': 
  1031.               multi=60*60*1000L; 
  1032.               break;
  1033.    }
  1034.    if(c>'9') s=s.substring(0,s.length()-1);
  1035.  }
  1036.  value=Integer.valueOf(s).intValue();
  1037.  return multi*value;
  1038. }
  1039. } /* class */
  1040.