import
authoradam <adam@megacz.com>
Tue, 13 Jul 2004 01:01:00 +0000 (01:01 +0000)
committeradam <adam@megacz.com>
Tue, 13 Jul 2004 01:01:00 +0000 (01:01 +0000)
darcs-hash:20040713010100-5007d-2256c9d3b25e7f77c4e19cd66fa94da7e00b3ff4.gz

Makefile [new file with mode: 0644]
src/org/ibex/jinetd/Host.java [new file with mode: 0644]
src/org/ibex/jinetd/Listener.java [new file with mode: 0644]
src/org/ibex/jinetd/Loader.java [new file with mode: 0644]
src/org/ibex/jinetd/Main.java [new file with mode: 0644]
src/org/ibex/jinetd/Port.java [new file with mode: 0644]
src/org/ibex/jinetd/Root.java [new file with mode: 0644]
src/org/ibex/jinetd/Watched.java [new file with mode: 0644]
src/org/ibex/jinetd/Watcher.java [new file with mode: 0644]
src/org/ibex/jinetd/Worker.java [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..d743c38
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,4 @@
+$(eval $(shell test -e Makefile.common || curl -O http://core.ibex.org/Makefile.common))
+include Makefile.common
+
+
diff --git a/src/org/ibex/jinetd/Host.java b/src/org/ibex/jinetd/Host.java
new file mode 100644 (file)
index 0000000..ade17d3
--- /dev/null
@@ -0,0 +1,26 @@
+package org.ibex.jinetd;
+import org.ibex.util.*;
+import java.io.*;
+import java.util.*;
+import java.lang.reflect.*;
+
+public class Host extends Loader {
+
+    final String hostname;
+    public Host(String path, String hostname) { super(path); this.hostname = hostname; }
+
+    public void changed(Watched w) {
+        super.changed(w);
+        Log.error(this, "changed(" + w + ")");
+        try {
+            ClassLoader cl = getClassLoader();
+            if (cl == null) return;
+            Class c = cl.loadClass("Main");
+            if (c == null) return;
+            Method m = c.getMethod("main", new Class[] { });
+            m.invoke(null, new Object[] { });
+        } catch (Exception e) {
+            Log.warn(this, "nope");
+        }
+    }
+}
diff --git a/src/org/ibex/jinetd/Listener.java b/src/org/ibex/jinetd/Listener.java
new file mode 100644 (file)
index 0000000..dfc5136
--- /dev/null
@@ -0,0 +1,15 @@
+package org.ibex.jinetd;
+import java.net.*;
+import org.ibex.net.*;
+import org.ibex.io.*;
+
+/**
+ *  jinetd will scan /jinetd/port/<num>/*.jar for a
+ *  class implementing this interface; instances must
+ *  have public constructors
+ */
+public interface Listener {
+    
+    public void accept(Connection c);
+
+}
diff --git a/src/org/ibex/jinetd/Loader.java b/src/org/ibex/jinetd/Loader.java
new file mode 100644 (file)
index 0000000..5c933c7
--- /dev/null
@@ -0,0 +1,192 @@
+package org.ibex.jinetd;
+import org.ibex.util.*;
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+
+/** represents a file or directory which is scanned for updates */
+public class Loader extends Watcher {
+
+    public Loader(String path) { super(path); }
+    
+    private TreeClassLoader classloader = new TreeClassLoader();
+    public ClassLoader getClassLoader() {
+        ClassLoader classloader = this.classloader;
+        if (classloader == null) {
+            classloader = this.classloader = new TreeClassLoader();
+            Log.warn(this, "getting classloader...");
+            try {
+                compileSource();
+            } catch (Exception e) {
+                Log.error(this, e);
+            }
+        }
+        return classloader;
+    }
+
+    private void fill(Vec vec, File dir) {
+        if (!dir.exists()) return;
+        if (!dir.isDirectory()) {
+            if (!dir.getPath().endsWith(".java")) return;
+            vec.addElement(dir.getAbsolutePath());
+        } else {
+            String[] list = dir.list();
+            for(int i=0; i<list.length; i++)
+                fill(vec, new File(dir.getAbsolutePath() + File.separatorChar + list[i]));
+        }
+    }
+    private void compileSource() throws Exception {
+        File srcdir = new File(this.path + File.separatorChar + "SRC");
+        if (!srcdir.exists()) return;
+        if (new File("/usr/bin/jikes").exists()) {
+            File bindir = new File(this.path + File.separatorChar + "BIN");  bindir.mkdirs();
+            File libdir = new File(this.path + File.separatorChar + "LIB");
+            String classpath = System.getProperty("java.class.path");
+            String [] l = new File("/jinetd/LIB/").list();
+            for(int i=0; i<l.length; i++) {
+                if (!l[i].endsWith(".jar")) continue;
+                classpath += File.pathSeparatorChar;
+                classpath += "/jinetd/LIB/" + l[i];
+            }
+            String bootclasspath = System.getProperty("sun.boot.class.path", "");
+            Vec args = new Vec();
+            args.addElement("/usr/bin/jikes");
+            args.addElement("+E");
+            args.addElement("-nowarn");
+            args.addElement("-bootclasspath");
+            args.addElement(bootclasspath);
+            args.addElement("-extdirs");
+            args.addElement(libdir.getAbsolutePath());
+            args.addElement("-classpath");
+            args.addElement(classpath);
+            args.addElement("-sourcepath");
+            args.addElement(srcdir.getAbsolutePath());
+            args.addElement("-d");
+            args.addElement(bindir.getAbsolutePath());
+            fill(args, srcdir);
+            String[] all = new String[args.size()];
+            args.copyInto(all);
+            Log.info(this, "invoking jikes");
+            for(int i=0; i<all.length; i++) Log.info(this, "   " + all[i]);
+            final Process jikes = Runtime.getRuntime().exec(all);
+            final BufferedReader out = new BufferedReader(new InputStreamReader(jikes.getInputStream()));
+            final BufferedReader err = new BufferedReader(new InputStreamReader(jikes.getErrorStream()));
+            new Thread() { public void run() {
+                try { for(String s = out.readLine(); s != null; s = out.readLine()) Log.info("jikes[stdout]", s); }
+                catch (Exception e) { Log.warn("jikes", e); } } }.start();
+            new Thread() { public void run() {
+                try { for(String s = err.readLine(); s != null; s = err.readLine()) Log.info("jikes[stderr]", s); }
+                catch (Exception e) { Log.warn("jikes", e); } } }.start();
+            jikes.waitFor();
+        } else {
+            Log.error(this, "ACK! jikes not found, javac not (yet) supported");
+        }
+    }
+
+    // only watch SRC and LIB for changes
+    public Watched slash(String path) {
+        return (path.equals("LIB") ||
+                path.equals("BIN") ||
+                path.equals("SRC") ||
+                path.endsWith(".jar") ) ? super.slash(path) : null; }
+
+
+    // dump the classloader if anything changes
+    public void changed(Watched w) {
+        if (w.path.indexOf("BIN") != -1) return;
+        if (classloader != null) {
+            Log.info(this, "Reloading all classes: " + path);
+            classloader = null;
+        }
+    }
+    
+    private class TreeClassLoader extends ClassLoader {
+        private Hashtable cache = new Hashtable();
+
+        private synchronized Class defineClass(String name) {
+            // first see if it's just sitting there
+            File classFile = slash("BIN").slash(name.replace('.', File.separatorChar) + ".class");
+            if (classFile.exists()) {
+                try {
+                    FileInputStream fis = new FileInputStream(classFile);
+                    byte[] b = InputStreamToByteArray.convert(fis);
+                    fis.close();
+                    Log.debug(this, "  loading " + name + " from " + classFile.getAbsolutePath());
+                    return defineClass(b, 0, b.length);
+                } catch (Exception e) {
+                    Log.warn(this, e);
+                }
+            }
+
+            // then scan the jarfiles for it
+            File lib = slash("LIB");
+            if (lib.exists() && lib.isDirectory()) {
+                try {
+                    String[] paths = lib.list();
+                    for(int i=0; i<paths.length; i++) {
+                        if (paths[i].endsWith(".jar")) {
+                            File f = new File(getAbsolutePath()+File.separatorChar+"LIB"+File.separatorChar+paths[i]);
+                            //Log.debug(this, "  scanning " + f.getAbsolutePath());
+                            ZipFile zf = new ZipFile(f);
+                            ZipEntry ze = zf.getEntry(name.replace('.', File.separatorChar) + ".class");
+                            if (ze != null) {
+                                byte[] b = InputStreamToByteArray.convert(zf.getInputStream(ze));
+                                Log.debug(this, "    loading " + name + " from " + f.getAbsolutePath());
+                                zf.close();
+                                return defineClass(b, 0, b.length);
+                            }
+                            zf.close();
+                        }
+                    }
+                } catch (Exception e) {
+                    Log.warn(this, e);
+                }
+
+            // finally scan ourselves
+                try {
+                    String[] paths = list();
+                    for(int i=0; i<paths.length; i++) {
+                        if (paths[i].endsWith(".jar")) {
+                            File f = new File(getAbsolutePath()+File.separatorChar+paths[i]);
+                            //Log.debug(this, "  scanning " + f.getAbsolutePath());
+                            ZipFile zf = new ZipFile(f);
+                            ZipEntry ze = zf.getEntry(name.replace('.', File.separatorChar) + ".class");
+                            if (ze != null) {
+                                byte[] b = InputStreamToByteArray.convert(zf.getInputStream(ze));
+                                Log.debug(this, "    loading " + name + " from " + f.getAbsolutePath());
+                                zf.close();
+                                return defineClass(b, 0, b.length);
+                            }
+                            zf.close();
+                        }
+                    }
+                } catch (Exception e) {
+                    Log.warn(this, e);
+                }
+
+            }
+                
+            // finally, resort to compiling it if we have to
+            //File src = new File(getAbsolutePath() + File.separatorChar + "SRC");
+            // FIXME
+            //if (!sourcebuilt) buildSource();
+            return null;
+        }
+
+        public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
+            try {
+                Class c = findSystemClass(name);
+                if (c != null) { if (resolve) resolveClass(c); return c; }
+            } catch (ClassNotFoundException e) { /* DELIBERATE */ }
+            Class c = (Class)cache.get(name);
+            if (c == null) {
+                //Log.info(this, "looking for class " + name);
+                c = defineClass(name);
+                if (c == null) throw new ClassNotFoundException();
+                cache.put(name, c);
+            }
+            if (resolve) resolveClass(c);
+            return c;
+        }
+    }
+}
diff --git a/src/org/ibex/jinetd/Main.java b/src/org/ibex/jinetd/Main.java
new file mode 100644 (file)
index 0000000..e32a32c
--- /dev/null
@@ -0,0 +1,18 @@
+package org.ibex.jinetd;
+import org.ibex.util.*;
+import java.io.*;
+import java.util.*;
+
+public class Main {
+
+    public static Root root = new Root(System.getProperty("jinetd.root", "/jinetd"));
+    public static void main(String[] s) throws Exception {
+        Log.color = true;
+        while(true) try {
+            Thread.sleep(1000);
+            //Log.info(Main.class, "scanning...");
+            if (root != null) root.scan();
+        } catch (Exception e) { Log.error(Main.class, e); }
+    }
+
+}
diff --git a/src/org/ibex/jinetd/Port.java b/src/org/ibex/jinetd/Port.java
new file mode 100644 (file)
index 0000000..2aded0a
--- /dev/null
@@ -0,0 +1,104 @@
+package org.ibex.jinetd;
+import org.ibex.util.*;
+import org.ibex.io.*;
+import java.io.*;
+import java.util.*;
+import java.net.*;
+import java.util.zip.*;
+
+public class Port extends Loader {
+
+    private final NetworkInterface iface;
+    private final int port;
+    private final Thread listener;
+
+    public Port(String path, NetworkInterface iface, int port) {
+        super(path);
+        this.iface = iface;
+        this.port = port;
+        this.listener = new PortThread();
+        listener.start();
+    }
+
+    public void changed(Watched w) {
+        //Log.warn(this, "Port: noticed change in " + w);
+        super.changed(w);
+    }
+
+    void dispatch(final Connection conn) throws Exception {
+        getClassLoader();
+        String[] list = list();
+        for(int i=0; i<list.length; i++) {
+            if (!list[i].endsWith(".jar")) continue;
+            //Log.warn(this, "checking " + (this.path + File.separatorChar + list[i]));
+            File f = new File(this.path + File.separatorChar + list[i]);
+            ZipInputStream zis = new ZipInputStream(new FileInputStream(f));
+            for(ZipEntry ze = zis.getNextEntry(); ze != null; ze = zis.getNextEntry()) {
+                String name = ze.getName();
+                if (name.endsWith(".class"))
+                    dispatch(conn, name.substring(0, name.length() - ".class".length()).replace('/', '.'));
+            }
+        }
+        check(conn, new File(getAbsolutePath() + File.separatorChar + "BIN"));
+    }
+
+    void check(Connection conn, File f) throws Exception {
+        //Log.warn(this, "check(" + f.getAbsolutePath() + ")");
+        if (!f.exists()) return;
+        if (!f.isDirectory()) {
+            if (!f.getAbsolutePath().endsWith(".class")) return;
+            String name = f.getAbsolutePath().substring(getAbsolutePath().length() + 5);
+            name = name.substring(0, name.length() - ".class".length()).replace(File.separatorChar, '.');
+            dispatch(conn, name);
+        } else {
+            String[] list = f.list();
+            for(int i=0; i<list.length; i++) check(conn, new File(f.getAbsolutePath() + File.separatorChar + list[i]));
+        }
+    }
+    void dispatch(final Connection conn, String name) throws Exception {
+        //Log.info(this, "attempting class " + name);
+        try {
+            Class c = getClassLoader().loadClass(name);
+            if (c != null) {
+                if (Listener.class.isAssignableFrom(c)) {
+                    Log.error(this, "dispatching connection on port " + port + " to " +
+                              c.getName());
+                    final Listener l = (Listener)c.newInstance();
+                    new Thread() { public void run() {
+                        Log.clearnotes();
+                        try {
+                            l.accept(conn);
+                        } catch (Exception e) {
+                            Log.error(l.getClass(), "Listener threw exception");
+                            Log.error(l.getClass(), e);
+                        } finally {
+                            conn.close();
+                        }
+                    } }.start();
+                    return;
+                }
+            }
+        } catch (Exception e) {
+            Log.error(this, e);
+        }
+    }
+
+    private class PortThread extends Thread {
+        public void run() {
+            try {
+                Log.warn(this, "Now listening on interface " + iface + ", port " + port);
+                ServerSocket ss = new ServerSocket(port);
+                for(Socket s = ss.accept(); ; s = ss.accept()) try {
+                    Log.warn(this, "accepted connection on port " + port);
+                    dispatch(new Connection(s, "megacz.com"));
+                    Log.warn(this, "done searching for service on port " + port);
+                } catch (Exception e) { Log.warn(Port.class, e); }
+            } catch (Exception e) { Log.error(Port.class, e);
+            } catch (Throwable t) {
+                Log.error(this, "serious error, aborting VM");
+                Log.error(this, t);
+                Root.reboot();
+            }
+        }
+    }
+}
diff --git a/src/org/ibex/jinetd/Root.java b/src/org/ibex/jinetd/Root.java
new file mode 100644 (file)
index 0000000..b844c9b
--- /dev/null
@@ -0,0 +1,62 @@
+package org.ibex.jinetd;
+import org.ibex.util.*;
+import java.io.*;
+import java.util.*;
+import java.net.*;
+
+public class Root extends Loader {
+
+    private final Host host;
+    private final Watched port;
+
+    public Root(String path) {
+        super(path);
+        host = new Host(path + File.separatorChar + "host", null);
+        port = new PortDir(path + File.separatorChar + "port");
+    }
+
+    public Watched slash(String part) {
+        if (part.equals("host")) return host;
+        if (part.equals("port")) return port;
+        if (part.equals("LIB")) return super.slash(part);
+        return null;
+    }
+
+    public static void reboot() {
+        Log.flush();
+        System.exit(0);
+    }
+
+    public void changed(Watched w) {
+        if (w.part.equals("host")) {
+            Log.warn(this, "/host changed");
+        } else if (w.part.equals("port")) {
+            Log.warn(this, "/port changed");
+        } else if (w.getAbsolutePath().startsWith("/jinetd/LIB/")) {
+            if (w.lastModifiedAtLastScan != -1) {
+                Log.error(this, "jinetd upgraded; bouncing the JVM....");
+                reboot();
+            }
+        } else {
+            Log.info(this, "unknown directory " + w.part + " changed");
+        }
+    }
+
+    private static class PortDir extends Watched {
+        public PortDir(String path) { super(path); }
+        public Watched slash(String part) {
+            String ipaddr  = part.indexOf(':') == -1 ? null : part.substring(0, part.indexOf(':'));
+            String portnum = part.indexOf(':') == -1 ? part : part.substring(part.indexOf(':') + 1);
+            try {
+                return new Port(this.path + File.separatorChar + part,
+                                ipaddr == null ? null : NetworkInterface.getByInetAddress(InetAddress.getByName(ipaddr)),
+                                portnum.equals("*") ? 0 : Integer.parseInt(portnum));
+            } catch (UnknownHostException e) {  Log.warn(this, "can't resolve host for port directory: " + part);
+            } catch (NumberFormatException e) { Log.warn(this, "invalid port directory: " + part);
+            } catch (Exception e) {             Log.warn(this, "error instantiating Port: " + part);
+            }
+            return null;
+        }
+    }
+
+}
diff --git a/src/org/ibex/jinetd/Watched.java b/src/org/ibex/jinetd/Watched.java
new file mode 100644 (file)
index 0000000..5d280c6
--- /dev/null
@@ -0,0 +1,54 @@
+package org.ibex.jinetd;
+import org.ibex.util.*;
+import java.io.*;
+import java.util.*;
+
+public class Watched extends File {
+
+    // Instance //////////////////////////////////////////////////////////////////////////////
+    
+    private Hashtable cache = new Hashtable();
+    long lastModifiedAtLastScan = -1;
+    public final String path;
+    public final String part;
+
+    public Watcher watcher() { return ((Watched)all.get(getParent())).watcher(); }
+    public Watched slash(String part) { return get(this.path + File.separatorChar + part); }
+    public void scan() throws IOException {
+        if (!exists())                                { return; }
+        if (lastModifiedAtLastScan != lastModified()) { watcher().changed(this); lastModifiedAtLastScan = lastModified(); }
+        if (!isDirectory())                           { return; }
+        Vec removals = new Vec();
+        for(Iterator i = cache.values().iterator(); i.hasNext();) {
+            Watched w = ((Watched)i.next());
+            if (w.exists()) w.scan();
+            else { watcher().changed(w); removals.addElement(w.path.substring(this.path.length() + 1)); }
+        }
+        for(int i=0; i<removals.size(); i++) cache.remove(removals.elementAt(i));
+        String[] kids = list();        
+        if (kids == null) return;
+        for(int i=0; i<kids.length; i++) {
+            if (cache.get(kids[i]) != null) continue;
+            Watched kid = slash(kids[i]);
+            if (kid == null) continue;
+            cache.put(kids[i], kid);
+            watcher().changed(kid);
+        }
+    }
+
+
+    // Pooling //////////////////////////////////////////////////////////////////////////////
+
+    private static WeakHashMap all = new WeakHashMap();
+    protected Watched(String path) {
+        super(path);
+        this.path = path;
+        this.part = path.substring(path.lastIndexOf(File.separatorChar) + 1);
+        all.put(path, this);
+    }
+    private static Watched get(String path) {
+        Watched ret = (Watched)all.get(path);
+        if (ret == null) ret = new Watched(path);
+        return ret;
+    }
+}
diff --git a/src/org/ibex/jinetd/Watcher.java b/src/org/ibex/jinetd/Watcher.java
new file mode 100644 (file)
index 0000000..86b1958
--- /dev/null
@@ -0,0 +1,10 @@
+package org.ibex.jinetd;
+import org.ibex.util.*;
+import java.io.*;
+import java.util.*;
+
+public abstract class Watcher extends Watched {
+    protected Watcher(String path) { super(path); }
+    public abstract void changed(Watched w);
+    public Watcher watcher() { return this; }
+}
diff --git a/src/org/ibex/jinetd/Worker.java b/src/org/ibex/jinetd/Worker.java
new file mode 100644 (file)
index 0000000..a43cb6e
--- /dev/null
@@ -0,0 +1,15 @@
+package org.ibex.jinetd;
+import org.ibex.util.*;
+import org.ibex.io.*;
+import org.ibex.net.*;
+import java.net.*;
+import java.io.*;
+import java.util.*;
+
+public interface Worker {
+    public void handleRequest(Connection c);
+}
+
+
+
+