allowed classloader-sharing between ports
[org.ibex.jinetd.git] / src / org / ibex / jinetd / Port.java
1 package org.ibex.jinetd;
2 import org.ibex.util.*;
3 import org.ibex.io.*;
4 import java.io.*;
5 import java.util.*;
6 import java.net.*;
7 import java.util.zip.*;
8
9 public class Port extends Loader {
10
11     final InetAddress bindTo;
12     final int port;
13
14     private static Hash cache = new Hash();
15     public static Port newPort(String path, InetAddress bindTo, int port) throws IOException {
16         String canonical = new File(path).getCanonicalPath();
17         Port p = (Port)cache.get(canonical);
18         if (p == null) cache.put(canonical, p = new Port(path, bindTo, port));
19         else Log.warn(Port.class, "   sharing " + bindTo+":"+port+" -> "+ (p.bindTo+":"+p.port));
20         p.spawn(bindTo, port);
21         return p;
22     }
23
24     void spawn(InetAddress bindTo, int port) { new PortThread(bindTo, port).start(); }
25     private Port(String path, InetAddress bindTo, int port) {
26         super(path);
27         this.bindTo = bindTo;
28         this.port = port;
29     }
30
31     public void changed(Watched w) {
32         //Log.warn(this, "Port: noticed change in " + w);
33         super.changed(w);
34     }
35
36     boolean dispatch(final Connection conn) throws Exception {
37         getClassLoader();
38         String[] list = list();
39         for(int i=0; i<list.length; i++) {
40             if (!list[i].endsWith(".jar")) continue;
41             //Log.warn(this, "checking " + (this.path + File.separatorChar + list[i]));
42             File f = new File(this.path + File.separatorChar + list[i]);
43             FileInputStream fis = null;
44             try {
45                 fis = new FileInputStream(f);
46                 ZipInputStream zis = new ZipInputStream(fis);
47                 for(ZipEntry ze = zis.getNextEntry(); ze != null; ze = zis.getNextEntry()) {
48                     String name = ze.getName();
49                     if (name.endsWith(".class"))
50                         if (dispatch(conn, name.substring(0, name.length() - ".class".length()).replace('/', '.')))
51                             return true;
52                 }
53             } finally { if (fis != null) fis.close(); }
54         }
55         if (check(conn, new File(getAbsolutePath() + File.separatorChar + "BIN"))) return true;
56         return false;
57     }
58
59     boolean check(Connection conn, File f) throws Exception {
60         if (!f.exists()) return false;
61         if (!f.isDirectory()) {
62             if (!f.getAbsolutePath().endsWith(".class")) return false;
63             String name = f.getAbsolutePath().substring(getAbsolutePath().length() + 5);
64             name = name.substring(0, name.length() - ".class".length()).replace(File.separatorChar, '.');
65             if (dispatch(conn, name)) return true;
66         } else {
67             String[] list = f.list();
68             for(int i=0; i<list.length; i++) 
69                 if (check(conn, new File(f.getAbsolutePath() + File.separatorChar + list[i])))
70                     return true;
71         }
72         return false;
73     }
74
75     boolean dispatch(final Connection conn, String name) throws Exception {
76         try {
77             final ClassLoader cl = getClassLoader();
78             final Class c = cl.loadClass(name);
79             if (c == null) return false;
80             if (!(Listener.class.isAssignableFrom(c) && c != Listener.class)) return false;
81             Log.info(this, "dispatching connection on port " + port + " to " + c.getName());
82             new Thread(tg, new Runnable() { public void run() {
83                 Log.clearnotes();
84                 Thread.currentThread().setContextClassLoader(cl);
85                 try {
86                     Listener l = (Listener)c.newInstance();
87                     l.accept(conn);
88                 } catch (Exception e) {
89                     Log.error(c, "Listener threw exception");
90                     Log.error(c, e);
91                 } finally {
92                     conn.close();
93                 }
94             } }).start();
95             return true;
96         } catch (Exception e) { Log.error(this, e); }
97         return false;
98     }
99
100     private class PortThread extends Thread {
101         InetAddress bindTo;
102         int port;
103         public PortThread(InetAddress bindTo, int port) { this.bindTo = bindTo; this.port = port; }
104         public void run() {
105             try {
106                 Log.warn(this, "Now listening on address " + (bindTo == null ? "all interfaces" : bindTo.toString()) +
107                          ", port " + port);
108                 ServerSocket ss = bindTo == null ? new ServerSocket(port) : new ServerSocket(port, 0, bindTo);
109                 for(Socket s = ss.accept(); ; s = ss.accept()) try {
110                     if (!dispatch(new Connection(s, "megacz.com"))) {
111                         Log.warn(this, "no handler for connection on port " + port);
112                         s.close();
113                     }
114                 } catch (Exception e) { Log.warn(Port.class, e); }
115             } catch (Exception e) { Log.error(Port.class, e);
116             } catch (Throwable t) {
117                 Log.error(this, "serious error, aborting VM");
118                 Log.error(this, t);
119                 Root.reboot();
120             }
121         }
122     }
123 }