added a FEATURE comment
[org.ibex.jinetd.git] / src / org / ibex / jinetd / Port.java
1 // Copyright 2000-2005 the Contributors, as shown in the revision logs.
2 // Licensed under the Apache Public Source License 2.0 ("the License").
3 // You may not use this file except in compliance with the License.
4
5 package org.ibex.jinetd;
6 import org.ibex.util.*;
7 import org.ibex.io.*;
8 import java.io.*;
9 import java.util.*;
10 import java.net.*;
11 import java.util.zip.*;
12
13
14 // Feature: port-level redirects
15 public class Port extends Loader {
16
17     final InetAddress bindTo;
18     final int port;
19
20     private static Hash cache = new Hash();
21     public static Port newPort(String path, InetAddress bindTo, int port) throws IOException {
22         String canonical = new File(path).getCanonicalPath();
23         Port p = (Port)cache.get(canonical);
24         if (p == null) cache.put(canonical, p = new Port(path, bindTo, port));
25         else Log.warn(Port.class, "   sharing " + bindTo+":"+port+" -> "+ (p.bindTo+":"+p.port));
26         p.spawn(bindTo, port);
27         return p;
28     }
29
30     void spawn(InetAddress bindTo, int port) { new PortThread(bindTo, port).start(); }
31     private Port(String path, InetAddress bindTo, int port) {
32         super(path);
33         this.bindTo = bindTo;
34         this.port = port;
35     }
36
37     public void changed(Watched w) {
38         //Log.warn(this, "Port: noticed change in " + w);
39         super.changed(w);
40     }
41
42
43     Class isListener(String name) throws ClassNotFoundException {
44         final ClassLoader cl = getClassLoader();
45         final Class c = cl.loadClass(name);
46         if (c == null) return null;
47         if (Listener.class.isAssignableFrom(c) && c != Listener.class) return c;
48         return null;
49     }
50
51     Class findListener() throws Exception {
52         getClassLoader();
53         String[] list = list();
54         for(int i=0; i<list.length; i++) {
55             if (!list[i].endsWith(".jar")) continue;
56             //Log.warn(this, "checking " + (this.path + File.separatorChar + list[i]));
57             File f = new File(this.path + File.separatorChar + list[i]);
58             FileInputStream fis = null;
59             try {
60                 fis = new FileInputStream(f);
61                 ZipInputStream zis = new ZipInputStream(fis);
62                 for(ZipEntry ze = zis.getNextEntry(); ze != null; ze = zis.getNextEntry()) {
63                     String name = ze.getName();
64                     if (name.endsWith(".class")) {
65                         String classname = name.substring(0, name.length() - ".class".length()).replace('/', '.');
66                         Class c = isListener(classname);
67                         if (c != null) return c;
68                     }
69                 }
70             } finally { if (fis != null) fis.close(); }
71         }
72         return findListener(new File(getAbsolutePath() + File.separatorChar + "BIN"));
73     }
74
75     Class findListener(File f) throws Exception {
76         if (!f.exists()) return null;
77         if (!f.isDirectory()) {
78             if (!f.getAbsolutePath().endsWith(".class")) return null;
79             String name = f.getAbsolutePath().substring(getAbsolutePath().length() + 5);
80             name = name.substring(0, name.length() - ".class".length()).replace(File.separatorChar, '.');
81             Class c = isListener(name);
82             if (c != null) return c;
83         } else {
84             String[] list = f.list();
85             for(int i=0; i<list.length; i++) {
86                 String classname = f.getAbsolutePath() + File.separatorChar + list[i];
87                 Class c = findListener(new File(classname));
88                 if (c != null) return c;
89             }
90         }
91         return null;
92     }
93
94     void dispatch(final Connection conn) throws Exception {
95         new Thread(tg, new Runnable() { public void run() {
96             String local = conn.getLocalAddress() + ":" + conn.getLocalPort();
97             String remote = conn.getRemoteHostname() + ":" + conn.getRemotePort();
98             try {
99                 Class c = findListener();
100                 if (c == null) throw new RuntimeException("couldn't find listener");
101                 Log.info("["+local+"]", "connection from " + remote + " => " + c.getName());
102                 Log.clearnotes();
103                 final ClassLoader cl = getClassLoader();
104                 Thread.currentThread().setContextClassLoader(cl);
105                 Listener l = (Listener)c.newInstance();
106                 l.accept(conn);
107             } catch (org.ibex.io.Stream.EOF eof) {
108                 Log.warn(this, "end of stream reached handling connection from " +
109                          conn.getRemoteHostname() + ":" + conn.getRemotePort());
110             } catch (Exception e) {
111                 Log.error(this, e);
112                 conn.close();
113             } finally {
114                 conn.close();
115             }
116         } }).start();
117     }
118
119     private class PortThread extends Thread {
120         InetAddress bindTo;
121         int port;
122         public PortThread(InetAddress bindTo, int port) { this.bindTo = bindTo; this.port = port; }
123         public void run() {
124             try {
125                 Log.warn(this, "Now listening on address " + (bindTo == null ? "all interfaces" : bindTo.toString()) +
126                          ", port " + port);
127                 ServerSocket ss = bindTo == null ? new ServerSocket(port) : new ServerSocket(port, 0, bindTo);
128                 for(Socket s = ss.accept(); ; s = ss.accept()) try {
129                     dispatch(new Connection(s, "megacz.com"));
130                 } catch (Exception e) { Log.warn(Port.class, e); }
131             } catch (Exception e) { Log.error(Port.class, e);
132             } catch (Throwable t) {
133                 Log.error(this, "serious error, aborting VM");
134                 Log.error(this, t);
135                 Root.reboot();
136             }
137         }
138     }
139 }