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.
5 package org.ibex.jinetd;
6 import org.ibex.util.*;
11 import java.util.zip.*;
14 // Feature: port-level redirects
15 public class Port extends Loader {
17 final InetAddress bindTo;
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);
30 void spawn(InetAddress bindTo, int port) { new PortThread(bindTo, port).start(); }
31 private Port(String path, InetAddress bindTo, int port) {
37 public void changed(Watched w) {
38 //Log.warn(this, "Port: noticed change in " + w);
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;
51 Class findListener() throws Exception {
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;
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;
70 } finally { if (fis != null) fis.close(); }
72 return findListener(new File(getAbsolutePath() + File.separatorChar + "BIN"));
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;
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;
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();
99 Class c = findListener();
100 if (c == null) throw new RuntimeException("couldn't find listener");
101 Log.info("["+local+"]", "connection from " + remote + " => " + c.getName());
103 final ClassLoader cl = getClassLoader();
104 Thread.currentThread().setContextClassLoader(cl);
105 Listener l = (Listener)c.newInstance();
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) {
119 private class PortThread extends Thread {
122 public PortThread(InetAddress bindTo, int port) { this.bindTo = bindTo; this.port = port; }
125 Log.warn(this, "Now listening on address " + (bindTo == null ? "all interfaces" : bindTo.toString()) +
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");