home *** CD-ROM | disk | FTP | other *** search
Java Source | 2001-01-26 | 24.0 KB | 889 lines |
- /*
- * Smart Cache, http proxy cache server
- * Copyright (C) 1998, 1999, 2000 Radim Kolar
- *
- * Smart Cache is Open Source Software; you may redistribute it
- * and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation; either
- * version 2, or (at your option) any later version.
- *
- * This program distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * A copy of the GNU General Public License is available as
- * /usr/doc/copyright/GPL in the Debian GNU/Linux distribution or on
- * the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
- * can also obtain it by writing to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
- import java.io.*;
- import java.util.*;
- import java.util.zip.*;
- import java.net.*;
-
- public final class cacheobject{
-
- public static final String RESERVED="<>";
- /* statistics */
- public static int c_hit,c_miss,c_refresh,c_block;
- public static int b_hit,b_miss;
-
- /* static ! */
- public static boolean end_dot;
- public static boolean keep_deleted;
- public static int generate_lastmod;
- public static boolean hide_errors;
- public static boolean escape_backslash;
- public static String custom500;
- public static String defaultname;
- public static boolean auto_compress;
- public static boolean auto_decompress;
-
- private String name; /* real name of object */
- private String localname; /* stored in file name */
- private String location; /* for redirects */
-
- private int size,httprc;
- private long expires, date, lastmod, lru;
- private cachedir dir;
- private String ctype; /* Content-Type */
- private String encoding; /* Content-Encoding */
- private boolean good; /* good or aborted download ? */
-
- cacheobject(String name, cachedir where)
- {
- this.name=name;
- dir=where;
- }
-
- cacheobject(String name,cachedir where,String ln,long lm,String ct, String enc,int size)
- {
- //if(name.equals(mgr.DEFAULTNAME)) name="";
- this(name,where);
- localname=ln;
- date=lastmod=lm;
- location=null;
- lru=System.currentTimeMillis();
- ctype=ct;
- encoding=enc;
- this.size=size;
- httprc=200;
- // dir.setDirty();
- good=true;
- }
-
- final public boolean equals(Object o)
- {
- if(o==null || ! (o instanceof cacheobject)) return false;
- cacheobject o1=(cacheobject)o;
-
- return name.equals(o1.getName());
- }
-
- final public int hashCode()
- {
- return name.hashCode();
- }
-
- final public boolean needSave()
- {
- if(localname!=null) return true;
- else
- return false;
- }
-
- final public String getName()
- {
- return name;
- }
- cacheobject(DataInputStream is, cachedir d,byte version) throws IOException
- {
- this.dir=d;
- name=is.readUTF();
- ctype=is.readUTF();
- localname=is.readUTF();
- location=is.readUTF();
- if(location.length()==0) location=null;
- size=is.readInt();
- httprc=is.readInt();
- expires=is.readLong();
- date=is.readLong();
- lastmod=is.readLong();
- lru=is.readLong();
- if(version==3)
- {
- encoding=is.readUTF();
- if(encoding.length()==0) encoding=null;
- good=is.readBoolean();
- } else good=true;
- }
-
- final public void save(DataOutputStream os) throws IOException
- {
- if(localname==null) return;
-
- os.writeUTF(name);
- os.writeUTF(ctype);
- os.writeUTF(localname);
- if(location!=null) os.writeUTF(location);
- else
- os.writeUTF("");
- os.writeInt(size);
- os.writeInt(httprc);
- os.writeLong(expires);
- os.writeLong(date);
- os.writeLong(lastmod);
- os.writeLong(lru);
- if(encoding!=null) os.writeUTF(encoding); else os.writeUTF("");
- os.writeBoolean(good);
- }
-
- 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
- {
- lru=System.currentTimeMillis();
-
- /* novy objekt */
- if(localname==null) { load_object(r); return;}
-
- /* test na expiraci */
- if(needRefresh(r,reload_age,min_age,max_age,percent,expire_age,redir_age)) { refresh_object(r); return;}
-
- /* jinak poslat z cache */
- send_fromcache(r);
- }
-
-
- final private File genTmp()
- {
- File out;
- String ld=dir.getLocalDir();
- if(ld==null) return null;
- while(true)
- {
- out=new File(ld+(int)(Math.random()*100000000f)+".tmp");
- if(!out.exists()) break;
- }
- return out;
- }
-
- final public void compress(int level)
- {
- if(level<=0) return;
- if(good==false) return;
- if(encoding!=null) return; /* compressed! */
- if(!ctype.startsWith("text/")) return; /* not text */
- if(localname==null || localname.equals(RESERVED)) return;
- if(size<=512) return; /* waste of CPU cycles */
- File t=genTmp();
- int i;
- try
- {
- GZIPOutputStream gz=new GZIPOutputStream(new FileOutputStream(t),4096);
- DataInputStream sin=new DataInputStream(new BufferedInputStream(
- new FileInputStream(dir.getLocalDir()+localname),4096));
- byte b[]=new byte[4096];
- while(true)
- {
- /* precist */
- i=sin.read(b);
- if(i==-1) break;
- gz.write(b,0,i);
- }
-
- gz.close();
- sin.close();
- int sz;
- sz=(int)t.length();
- if(sz>=size-512) { t.delete();return;}
-
- String nm; /* new name */
- if(!localname.endsWith(".gz"))
- if(localname.endsWith("_r"))
- nm=genName(localname.substring(0,localname.length()-2)+".gz");
- else
- nm=genName(localname+".gz");
- else
- nm=localname;
-
- File n=new File(dir.getLocalDir()+nm);
- /* nm=genName(name)+".gz";
- if(n.exists() && !nm.equals(localname))
- { // regenerate name to avoid clashes
- nm=genName(nm.substring(1));
- n=new File(dir.getLocalDir()+nm);
- System.out.println("Regenerating name to "+n);
- }
- */
- /* rename to new */
- if(!rename(t,n)) {
- System.out.println("**COMPRESS ERROR** Rename "+t+" to "+n+" failed.");
- dir.dirty=true;
- return;
- }
- encoding="gzip";
- if(!localname.equals(nm)) new File(dir.getLocalDir()+localname).delete();
- localname=nm;
- date=System.currentTimeMillis();
- dir.dirty=true;
- System.out.println("Compressed: "+dir.getLocalDir()+localname+" ("+(size-sz)+" B, "+(int)(100*(1.0f-(float)sz/size))+"% saved)");
- size=sz;
- }
- catch (IOException j) {System.out.println("**COMPRESS ERROR** "+j);}
- }
-
-
- final public boolean isValid()
- {
- if(localname==null || localname.equals(RESERVED))
- {
- System.out.print("NULL filename");
- return false;
- }
- File f=new File( dir.getLocalDir()+localname );
- if(!f.isFile())
- {
- System.out.print("No such file");
- return false;
- }
- if(!f.canRead()) {
- System.out.print("No read access");
- return false;
- }
- // check obj. size
- if(f.length()!=size) {
- System.out.print("File size mismatch");
- return false;
- }
- //check obj. date
- if(f.lastModified()>date) {
- System.out.print("Modified after load");
- return false;
- }
- return true; // O.K. !
- }
-
- final public String getLocalName()
- {
- return localname;
- }
-
- final public void clearLocalName()
- {
- if(localname==null) return;
- new File(dir.getLocalDir()+localname).delete();
- localname=null;
- dir.dirty=true;
- }
-
- final public synchronized void regenName()
- {
- if(localname==null)
- {
- System.out.println("[DEBUG] regen name called on "+name+" with NULL localname!");
- }
- localname=genName(name);
- if(encoding==null) return;
-
- if(!localname.endsWith(".gz"))
- if(localname.endsWith("_r"))
- localname=genName(localname.substring(0,localname.length()-2)+".gz");
- else
- localname=genName(localname+".gz");
- }
- /* Generate unique filename */
- final public synchronized String genName(String tmp)
- {
- /* String tmp;
- tmp=name;
- */
- File f;
- String localdir=dir.getLocalDir();
-
- if(localdir==null) return null;
- // System.err.println("in="+name);
- int j=tmp.length()-1;
- if(j>=0)
- {
- byte v[];
- v=new byte[j+1];
- tmp.getBytes(0,j+1,v,0);
- /* jestlize mame ? a nejaky bordel za nim, uriznout! */
- loop1:for(int zz=0;zz<=j;zz++)
- {
- switch(v[zz])
- {
- case 0x3b: // ;
- case 0x3a: // :
- case 0x3d: // =
- case 0x3f: // ?
- tmp=tmp.substring(0,zz);
- // System.err.println("new tmp="+tmp);
- break loop1;
- }
- }
- }
- // System.err.println("in2="+tmp);
-
- /* pokud je toto redirect, pridat k jmenu _r */
- /* aby se to nemotalo do jmena adresare */
- if(httprc==301 || httprc==302 || httprc==307) tmp+="_r";
-
- /* nahrazujeme nepratelske znaky */
- // tmp=tmp.replace(':','_'); neni uz nutne
- tmp=tmp.replace('>','}');
- tmp=tmp.replace('<','{');
- // problems with OS/2
- tmp=tmp.replace('|','!');
- if(escape_backslash) tmp=tmp.replace('\\','-');
- if(tmp.length()==0) tmp=defaultname;
-
- /* Kill tecku if nedovoleno ! */
- if(!end_dot && tmp.endsWith(".")) tmp=tmp.substring(0,tmp.length()-1);
-
- f=new File(localdir+tmp);
- if(!f.exists()) return tmp;
- /* a nyni kolotoc! */
- int i=0;
- while(true)
- {
- f=new File(localdir+i+tmp);
- if(!f.exists()) break;
- i++;
- // if(i==1000) return null; CHECK: need to check??
- }
- return i+tmp;
- }
-
-
- /* L O A D ! */
-
- final private void load_object(request r) throws IOException
- {
- c_miss++;
- if(localname==null) localname=RESERVED; /* prevents from async dir cleaning */
- if(ctype==null) ctype="unknown/unknown";
- // httprc=500;
- String ln;
- r.add_ims();
- Socket toserver=null;
- dir.dirty=true;
- try{
- toserver=r.connectToHost();
- }
- catch (IOException ccc) { if(localname!=null && localname.equals(RESERVED)) localname=null;
- dir.dirty=true;
- /* POSLAT NOT MODIFIED! */
- if(r.getIms()>0) { r.make_headers(304,null,null,null,0,0,0);
- r.send_headers();
- return;
- }
-
- /* 500 RC komedie */
- if(custom500==null) r.send_error(500,"Proxy load failed ("+ccc+") and object not found in cache");
- else
- if(custom500.charAt(0)=='0') {
- r.make_headers(200,"image/gif",null,null,43,886828316241L,0);
- r.send_headers();
- r.sendBytes(r.GIF);
- } else
- if(custom500.charAt(0)=='-') {
- r.send_error(204,"No Content");
- }
- else if(custom500.charAt(0)=='!')
- {
- r.make_headers(200,"text/html",null,null,0,new Date().getTime(),0);
- r.send_headers();
- r.sendString("<HTML></HTML>");
- }
- else
- { r.make_headers(301,null,null,custom500,0,new Date().getTime(),0);r.send_headers();}
-
- r.close();
- return;
- }
-
- DataInputStream sin=new DataInputStream (new BufferedInputStream(toserver.getInputStream()));
- DataOutputStream sou=new DataOutputStream(new BufferedOutputStream(toserver.getOutputStream()));
- try
- {
- r.send_request(sou);
- r.read_headers(sin);
- }
- catch (IOException e)
- {
- if(localname!=null && localname.equals(RESERVED)) localname=null;
- dir.dirty=true;
- System.out.println("FAILED: "+dir.getLocalDir()+name+" ("+e+")");
- throw e;
- }
- r.send_headers();
- httprc=r.getRc();
- if(httprc==304) { /* avoid another Netscape BUG */
- if(localname!=null && localname.equals(RESERVED)) localname=null;
- dir.dirty=true;
- toserver.close();
- return;
- }
-
- File f=null;
- switch(httprc)
- {
- /* good RC */
- case 200: /* OK */
- case 203: /* Non-Authoritative Information */
- case 300: /* Multiple Choices */
- case 301: /* Moved Permanently */
- case 302: /* Moved Temporary */
- case 307: /* temporary redirect */
- case 410: /* Gone */
- /* bad RC */
- case 400: /* Bad request */
- case 404: /* not found */
- case 403: /* Forbidden */
- case 405: /* method not allowed */
- f=genTmp();
- break;
- default:
- // System.out.println("[debug] RC="+httprc+" on "+dir.getLocalDir()+name);
- }
- if(!r.canCache()) f=null; /* non cacheable request */
- DataOutputStream fout=null;
-
- try{
- if(f!=null) fout=new DataOutputStream(
- new BufferedOutputStream(new FileOutputStream(f),4096));
- else
- { /* not caching */
- fout=null;
- if(localname!=null && localname.equals(RESERVED)) localname=null;
- dir.dirty=true;
- }
- }
- catch (IOException e)
- { /* tmp file create error */
- fout=null;
- if(localname!=null && localname.equals(RESERVED)) localname=null;
- dir.dirty=true;
- }
-
- try{
- r.transfer_object(sin,fout,dir);
- }
- catch (IOException e)
- {
- if(f!=null) f.delete();
- if(localname!=null && localname.equals(RESERVED)) localname=null;
- dir.dirty=true;
- System.out.println("FAILED: "+dir.getLocalDir()+name+" ("+e+")");
- throw e;
- }
- finally
- {
- b_miss+=r.getCsize();
- }
- // sin.close();sou.close(); - not needed
- toserver.close();
- if(f==null) /* not cached, end. */
- {
- if(localname!=null && localname.equals(RESERVED)) localname=null;
- dir.dirty=true;
- return;
- }
- /* multiple downloads are active ? (stupid Netscape bug) */
- if(localname==null || localname.equals(RESERVED)) ln=genName(name);
- else
- ln=localname;
- File n=new File(dir.getLocalDir()+ln);
- if(!rename(f,n)) {
- System.out.println("**LOAD ERROR** Rename "+f+" to "+n+" failed.");
- if(localname!=null && localname.equals(RESERVED))
- {
- localname=null;
- n.delete();
- }
- dir.dirty=true;
- f.delete();
- return;
- }
- /* update object status */
- localname=ln;
- setInfo(r);
- System.out.println("Loaded: "+n+" (size="+size+" B)");
- good=true;
- if(auto_compress) compress(9);
- }
-
- /* R E F R E S H ! */
-
- final private void refresh_object(request r) throws IOException
- {
- Socket toserver=null;
- dir.dirty=true;
- try{
- toserver=r.connectToHost();
- }
- catch (IOException eee) { send_fromcache(r);return;}
-
- long clims=r.getIms();
- if(clims==0) clims=1; /* nema nic, to je to same jako kdyby mel hodne starou verzi */
- /* pokud by se nechalo na 0 - nedostal by OBJEKT! */
- if (clims>=lastmod) clims=0; /* ma pozdejsi verzi objektu nez my, posuzujeme to jako
- kdyby mel tu samou jako my, protoze IMS delame na nas objekt */
- r.add_ims(lastmod);
-
- DataInputStream sin=new DataInputStream (new BufferedInputStream(toserver.getInputStream()));
- DataOutputStream sou=new DataOutputStream(new BufferedOutputStream(toserver.getOutputStream()));
-
- try{
- r.send_request(sou);
- r.read_headers(sin);
- }
- catch (IOException e)
- {send_fromcache(r); return;}
-
- /* podivame se co prislo, pokud !=304, tak to posleme a nacachujeme */
- int rc=r.getRc();
- if(rc==304) {
- date=System.currentTimeMillis();
- dir.dirty=true;
- toserver.close();
- expires=r.getExpires(); // refresh possible Expires for Microsoft IIS 4.0
- System.out.println("Checked: "+dir.getLocalDir()+localname);
- if(clims>0) { /* musime poslat objekt z cache */
- r.clearIms();
- send_fromcache(r);
- return;}
- r.send_headers();
- c_hit++;
- return;
- }
- c_refresh++;
- if(httprc<400 && rc>=400) /* we have a GOOD object in cache, but got a wrong reply */
- {
-
- /* handle object deletion */
- if(rc==404 )
- if(keep_deleted==false) {
- // nothing special is required
- /*
- new File(dir.getLocalDir()+localname).delete();
- System.out.println("Deleted: "+dir.getLocalDir()+localname);
- localname=null;
- dir.dirty=true;
- */
- }
- else
- {
- System.out.println("Gone: "+dir.getLocalDir()+localname+" - sending cached copy");
- touch();
- try{
- toserver.close();
- }
- catch (IOException e) {}
-
- send_fromcache(r);
- return;
- }
- /* handle temporary remote server error */
- if(rc==400 || rc==403 || rc==408 || rc==405 /* timeout */ || rc>= 500)
- {
- if(rc<408) touch();
- if (hide_errors==true)
- {
- System.out.println("Error "+rc+":"+dir.getLocalDir()+localname+" - sending cached copy");
- try{
- toserver.close();
- }
- catch (IOException e) {}
- send_fromcache(r);
- return;
- }
- else /* hide errors == false , do not cache this ! */
- {
- System.out.println("Error "+rc+":"+dir.getLocalDir()+localname);
- r.nocache();
- }
- }
- } /* bad rc and good obj in cache */
- // continue with "normal way"
- r.send_headers();
-
- /* musime to ulozit na disk! */
- File f=null;
- switch(rc)
- {
- /* good RC */
- case 200: /* OK */
- case 203: /* Non-Authoritative Information */
- case 300: /* Multiple Choices */
- case 301: /* Moved Permanently */
- case 302: /* Moved Temporary */
- case 307: /* temporary redirect */
- case 410: /* Gone */
- /* bad RC */
- case 400: /* Bad request */
- case 404: /* not found */
- case 403: /* Forbidden */
- case 405: /* method not allowed */
- f=genTmp();
- break;
- }
-
- if(!r.canCache()) f=null; /* non cacheable request */
- DataOutputStream fout;
-
- try{
- if(f!=null) fout=new DataOutputStream(
- new BufferedOutputStream(new FileOutputStream(f),4096));
- else
- fout=null;
- }
- catch (IOException e) {fout=null;}
- try{
- r.transfer_object(sin,fout,dir);
- }
- catch (IOException e)
- {
- if(f!=null) f.delete();
- System.out.println("FAILED: "+dir.getLocalDir()+localname+" ("+e+")");
- throw e;
- }
- finally
- {
- b_miss+=r.getCsize();
- }
- sin.close();sou.close();toserver.close();
- if(f==null) return;
- /* stalled entry? - regenerate object name */
- if(localname==null || localname.equals(RESERVED)) localname=genName(name);
-
- File n=new File(dir.getLocalDir()+localname);
-
- /* rename to new */
- if(!rename(f,n)) {
- System.out.println("**REFRESH ERROR** Rename "+f+" to "+n+" failed.");
- dir.dirty=true;return;}
- /* update hlavicek */
- clims=(lastmod>0?lastmod:date);
- setInfo(r);
- long delta;
- if(lastmod>0) delta=(lastmod-clims)/60000L;
- else
- delta=(date-clims)/60000L;
-
- if(delta>60*48) System.out.println("Refreshed: "+n+" (delta="+delta/60/24+" d, size="+size+" B)");
- else
- if(delta>200) System.out.println("Refreshed: "+n+" (delta="+delta/60+" h, size="+size+" B)");
- else
- System.out.println("Refreshed: "+n+" (delta="+delta+" m, size="+size+" B)");
- if(auto_compress) compress(9);
- }
-
- final private synchronized boolean rename(File from, File to)
- {
- to.delete();
- boolean r=from.renameTo(to);
- if(!r && !to.exists()) localname=null;
- if(!r) from.delete();
- return r;
- }
-
- /* S E N D F R O M C A C H E */
-
- final private void send_fromcache(request r) throws IOException
- {
- dir.dirty=true;
- if(r.getIms()>0) { c_hit++;
- r.make_headers(304,null,null,null,0,0,0);
- r.send_headers();
- return;
- }
-
- /* otevrit soubor */
- InputStream fin=null;
- /* dekomprimovat */
- boolean decomp=false;
- if(encoding!=null && auto_decompress && ctype.startsWith("text/"))
- {
- if(r.getEncoding()==null) decomp=true;
- }
- try{
- fin=
- new BufferedInputStream(new FileInputStream(dir.getLocalDir()+localname),4096);
- if(decomp) fin=new java.util.zip.GZIPInputStream(fin,4096);
- }
- catch (IOException e) { System.out.println("Missing: "+dir.getLocalDir()+localname);
- localname=null;
- load_object(r);
- return;}
- c_hit++;
- b_hit+=size;
- if(generate_lastmod>0)
- {
- if(decomp) r.make_headers(httprc,ctype,null,location,0,lastmod==0? date:lastmod,expires);
- else
- r.make_headers(httprc,ctype,encoding,location,size,lastmod==0? date:lastmod,expires);
- }
- else
- {
- if(decomp)
- r.make_headers(httprc,ctype,null,location,0,lastmod,expires);
- else
- r.make_headers(httprc,ctype,encoding,location,size,lastmod,expires);
- }
- r.send_headers();
- if(r.getMethod()==httpreq.REQUEST_GET) r.transfer_object(fin,null,dir);
- }
-
- final private void setInfo(request r)
- {
- httprc=r.getRc();
- ctype=r.getCtype();
- encoding=r.getEncoding();
- location=r.getLocation();
- size=r.getCsize();
- lastmod=r.getIms();
- expires=r.getExpires();
-
- date=System.currentTimeMillis();
- /*
- if(lastmod>=date)
- {
- System.out.println("Warning Lastmod is in future! on "+dir.getLocalDir()+localname);
- if(lastmod-date<1000*60*180) date=lastmod+1000l;
- else
- lastmod=0;
- }
- */
- dir.dirty=true;
- }
-
- final private boolean needRefresh(request r,long reload_age,long min_age, long max_age, float percent,long expire_age,long redir_age)
- {
- // System.out.println("Checking "+dir.getLocalDir()+localname);
- long now=System.currentTimeMillis();
- long age=now-date;
- /* - test reload status */
- if(r.requestReload() && age>=reload_age)
- {
- // System.out.println("Refresh: Refreshed on user request > RELOAD_AGE");
- return true;
- }
-
- if(age>max_age)
- {
- // System.out.println("Refresh: AGE> MAXAGE "+age/60000+" "+max_age/60000);
- return true; /* puvodne to bylo az za expires ... */
- }
-
-
-
- if(expires!=0)
- {
- if(expires<=now && age>=expire_age)
- {
- // System.out.println("Expired...");
- return true;
- }
- else
- {
- // System.out.println("Not Yet Expired...");
- return false;
- }
- }
-
- /* redir rest */
- if(location!=null)
- if(age>=redir_age) return true;
- else return false;
-
- /* min age test */
- if(age<=min_age)
- {
- // System.out.println("No Refresh: AGE<= MINAGE "+age/60000+" "+min_age/60000);
- return false;
- }
-
- if(lastmod==0) return true; /* usetrime si nejake to pocitani */
-
- /* lastmod check */
- long lm_age=date - lastmod;
- float lmfactor=(float) age / (float)lm_age;
- // System.out.println("lmfactor="+lmfactor+" percent="+percent);
-
- if(lmfactor<percent && lmfactor>0) return false;
- else
- return true; /* refresh it! */
- }
-
- final public String toString()
- {
- return dir.getLocalDir()+name;
- }
-
- final public cachedir getDirectory()
- {
- return dir;
- }
-
- final public void setDirectory(cachedir newdir)
- {
- if(dir==null) throw new IllegalArgumentException("Directory must not be null");
- dir=newdir;
- }
-
- final public long getLRU()
- {
- return lru;
- }
-
- final public int getRC()
- {
- return httprc;
- }
-
- final public int getSize()
- {
- return size;
- }
-
- final public long getDate()
- {
- return date;
- }
-
- final public long getExp()
- {
- return expires;
- }
-
- final public void delete()
- {
- dir.remove(this);
- if(localname==null) return;
- // System.out.println("Deleted object: "+dir.getLocalDir()+localname);
- new File(dir.getLocalDir()+localname).delete();
- localname=null;
- }
-
- final void touch()
- {
- lru=date=System.currentTimeMillis();
- dir.dirty=true;
- }
-
- final void touchLRU()
- {
- lru=System.currentTimeMillis();
- dir.dirty=true;
- }
-
- final public boolean isCheckable()
- {
- if(lastmod==0) return false; else return true;
- }
- } /* class */
-