// ServletLoader.java
// $Id: LocalServletLoader.java,v 1.5 1997/08/22 08:48:16 abaird Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html

package w3c.jigsaw.servlet;

import java.io.*;
import java.net.*;
import java.util.*;

/**
 * LocalServletLoader : 
 * @author Benoit Mahe <bmahe@inria.fr>
 */

class ServletClassEntry {
  
  long   classStamp   = 0;
  Class  servletClass = null;
  File   classFile    = null;

  public boolean isModified() {
    return (classFile.lastModified() > classStamp);
  }

  public void update() {
    classStamp   = classFile.lastModified();
  }

  public ServletClassEntry (File classFile, Class servletClass) {
    this.classFile    = classFile;
    this.servletClass = servletClass;
    this.classStamp   = classFile.lastModified();
  }

}


public class LocalServletLoader extends ClassLoader {
  private static final boolean debug = false;
  private Hashtable classes = null;

  ServletWrapper wrapper = null;

  private void trace(String msg) {
    System.out.println("["+wrapper.getURLPath()+"]: "+msg);
  }

  /**
   * Given the class name, return its URL.
   * @param name The class to be loaded.
   * @return The URL for the class.
   */

  protected File locateClass(String name) {
    File base = wrapper.getServletDirectory();
    try {
      String cname = null;
      if (name.endsWith(".class")) {
	int idx = name.lastIndexOf(".");
	name = name.substring(0,idx);
      } 
      cname = name.replace('.', '/')+".class";
      return new File(base, cname);
    } catch (Exception ex) {
    }
    return null;
  }

    public URL getResource(String name) {
	String url = wrapper.getServletDirectory().getAbsolutePath();
	// Return the location of that resource (name resolved by j.l.Class)
	if ( File.separatorChar != '/' )
	    url = url.replace(File.separatorChar, '/');
	try {
	    return new URL("file", "localhost", url+"/"+name);
	} catch (MalformedURLException ex) {
	    // Shouldn't fall here
	    return null;
	}
    }

    /**
     * Get a resource as a stream.
     * @param name The name of the resource to locate.
     */

    public InputStream getResourceAsStream(String name) {
	try {
	    URLConnection c = getResource(name).openConnection();
	    return c.getInputStream();
	} catch (IOException ex) {
	    return null;
	}
    }

  protected Class loadClassFile(File file) 
    throws ClassNotFoundException
  {
    byte data[] = null;
    if ( file == null )
      throw new ClassNotFoundException("invalid servlet base");
    if ( debug )
      trace(" located at "+file);
    try {
      BufferedInputStream in      = 
	new BufferedInputStream( new FileInputStream( file) );
      ByteArrayOutputStream out   = new ByteArrayOutputStream(512);
      byte                  buf[] = new byte[512];
      for (int got = 0 ; (got = in.read(buf)) > 0 ; )
	out.write(buf, 0, got);
      data = out.toByteArray();
      in.close();
    } catch (Exception ex) {
      if (debug) 
	ex.printStackTrace();
      throw new ClassNotFoundException(file.getAbsolutePath());
    }
    // Define the class:
    return defineClass(data, 0, data.length);
  }

  protected Class loadClass(String name, boolean resolve)
    throws ClassNotFoundException
  {
    Class c = null;
    if ( debug )
      trace("loading "+name);

    // Look for a cached class first:
    ServletClassEntry entry = (ServletClassEntry)classes.get(name);
    if (entry != null) {
      if (entry.isModified()) {
	entry.servletClass =  loadClassFile(entry.classFile);
	if ( resolve )
	  resolveClass(entry.servletClass);
	entry.update();
      }
      return entry.servletClass;
    }

    SecurityManager s = System.getSecurityManager();
    if ( s != null ) {
      int i = name.lastIndexOf('.');
      if ( i >= 0 )
	s.checkPackageAccess(name.substring(0, i));
    }
    // Then look for a system class:
    try {
      if ((c = findSystemClass(name)) != null) {
    	return c;
      }
    } catch (Exception ex) {
    }
    File  file    = locateClass(name);
    c             = loadClassFile(file);
    if ( resolve )
      resolveClass(c);
    classes.put(name, new ServletClassEntry(file,c));
    if ( debug )
      trace(name+": loading done.");
    return c;
  }

  protected LocalServletLoader(ServletWrapper wrapper) {
    super();
    this.wrapper = wrapper;
    this.classes = new Hashtable(13);
  }

  protected LocalServletLoader(LocalServletLoader loader) {
    super();
    this.wrapper = loader.wrapper;
    this.classes = loader.classes;
  }

  public boolean classChanged(String name) {
    ServletClassEntry entry = (ServletClassEntry)classes.get(name);
    if (entry != null) {
      return entry.isModified();
    }
    return true;
  }

}
