package net;

import futils.Futil;

import java.io.*;
import java.util.StringTokenizer;

/**
 pass an instance of this via an ObjectOutputStream
 instance to the remote host and invoke loadIt().
 That will define the class.
 */

public class RemoteClassLoader
    extends ClassLoader
    implements Serializable {
  String className;
  byte byteCodes[];

  ClassLoader loader;

  public static RemoteClassLoader
      getRemoteClassLoader(Object o) {
    try {
      return new
          RemoteClassLoader(o);
    } catch (IOException e) {
      System.out.println("Could not make instance");
      e.printStackTrace();
      return null;
    }
  }

  public static void printPath() {
    String path = System.getProperty("java.class.path");
    String sep = System.getProperty("path.separator");
    StringTokenizer st = new StringTokenizer(path, sep);
    while (st.hasMoreElements())
      System.out.println(st.nextToken());
  }

  // strip package name and
  // add the .class suffix
  private String getFileName() {
    String sep = System.getProperty("path.separator");
    if (className.indexOf(".") < 0)
      return className + ".class";
    return
        utils.ReplaceString.sub(className, ".", sep)
        + ".class";
  }

  private File getClassFile() {
    File f = new File(getFileName());
    if (f.exists()) return f;
    // lets play find the file...
    // printPath();
    return
        Futil.getReadFile(getFileName()
                          + " not found, please help me find it!");
  }

  public RemoteClassLoader(Object o) throws IOException {
    Class c = o.getClass();
    className = c.getName();
    loader = c.getClassLoader();
    if (loader == null)
      System.out.println("Default system class loader");
    else
      System.out.println(loader);
    File classFile = getClassFile();
    System.out.println("file=" + classFile);
    FileInputStream fis =
        new FileInputStream(classFile);
    byteCodes = new byte[fis.available()];
    fis.read(byteCodes);
    fis.close();
    System.out.println("#bytes=" + byteCodes.length);
  }

  public void saveFileLocally() {
    try {
      FileOutputStream fos
          = new FileOutputStream(getFileName());
      fos.write(byteCodes);
      fos.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public RemoteClassLoader(String cn, byte bc[]) {
    className = cn;
    byteCodes = bc;
  }

  public void loadIt() {
    loadClass(className, true);
  }

  public void reload() {
    // force the class to be reloaded,
    // in case it has changed.
    try {
      Class c = defineClass(
          className, byteCodes, 0, byteCodes.length);
      System.out.println("resolving:" + className);
      resolveClass(c);
    } catch (Exception e) {
      saveFileLocally();
      //perhaps it can find it if it is
      // on disk...
    }
  }

  protected synchronized Class loadClass(String s, boolean b) {
    Class cl = findLoadedClass(s);
    try {
      if (cl == null)  // not in cache!
        return findSystemClass(s);
    } catch (ClassNotFoundException e) {
    }
    System.out.println("defining:" + s);
    Class c = defineClass(
        className, byteCodes, 0, byteCodes.length);
    System.out.println("resolving:" + className);
    if (c != null && b) resolveClass(c);
    return c;
  }

  public static void main(String args[]) {
    RemoteClassLoader
        rcl = RemoteClassLoader.getRemoteClassLoader(
            new ComputeMe());
    rcl.reload();
  }
}