/* * Copyright (c) 2010, Manuel Mausz. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import static java.lang.System.err; import static java.lang.System.out; import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.channels.DatagramChannel; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.Collections; import java.util.Properties; import java.util.ArrayList; import java.util.Calendar; import java.util.Iterator; import java.util.HashMap; import java.util.Arrays; import java.util.Map; import java.net.*; import java.io.*; /* * Proxy implementation for Lab#1 of DSLab WS10 * See angabe.pdf for details * * This code is not documented at all. This is volitional * * @author Manuel Mausz (0728348) */ public class Proxy { public class FSRecord implements Comparable { public final String host; public final int port; public int usage; public boolean online; public long lastUpdate; FSRecord(String host, int port) { this.host = host; this.port = port; usage = 0; online = true; lastUpdate = Calendar.getInstance().getTimeInMillis(); } public void ping() { online = true; lastUpdate = Calendar.getInstance().getTimeInMillis(); } public boolean equals(FSRecord o) { return online == o.online && usage == o.usage; } public int compareTo(FSRecord o) { return usage - o.usage; } } public class FSRecords extends HashMap {} /*==========================================================================*/ public class UserRecord { public final String name; public final String pass; public int credits; public ArrayList loggedin; UserRecord(String name, String pass) { this.name = name; this.pass = pass; credits = 0; loggedin = new ArrayList(); } } public class UserRecords extends HashMap {} /*==========================================================================*/ public class ProxyConnection implements Runnable { private final String host; private final int port; private FSRecords fileservers; ProxyConnection(String host, int port, FSRecords fileservers) { this.host = host; this.port = port; this.fileservers = fileservers; } /*------------------------------------------------------------------------*/ public void run() { String key = host + ":" + port; synchronized(fileservers) { FSRecord record = fileservers.get(key); if (record == null) { fileservers.put(key, new FSRecord(host, port)); out.println("New fileserver registered: " + key); } else { if (!record.online) out.println("Fileserver is online again: " + key); record.ping(); } } } } /*==========================================================================*/ public class UDPSocketReader implements Runnable { private final DatagramChannel dchannel; private FSRecords fileservers; private final Object mainLock; private final ExecutorService pool; UDPSocketReader(DatagramChannel dchannel, FSRecords fileservers, Object mainLock) { this.dchannel = dchannel; this.fileservers = fileservers; this.mainLock = mainLock; this.pool = Executors.newCachedThreadPool(); } /*------------------------------------------------------------------------*/ public void run() { try { ByteBuffer buffer = ByteBuffer.allocate(4); while(true) { buffer.clear(); InetSocketAddress proxyaddr = (InetSocketAddress) dchannel.receive(buffer); try { pool.execute(new ProxyConnection(proxyaddr.getHostName(), buffer.getInt(0), fileservers)); } catch(IndexOutOfBoundsException e) { /* simple ignore that packet */ } } } catch(IOException e) { /* ignore that exception * thread will shutdown and unlock the main thread * which will shutdown the application */ } pool.shutdown(); try { if (!pool.awaitTermination(100, TimeUnit.MILLISECONDS)) out.println("Trying to shutdown the UDP Proxy connections. This may take up to 15 seconds..."); if (!pool.awaitTermination(5, TimeUnit.SECONDS)) { pool.shutdownNow(); if (!pool.awaitTermination(5, TimeUnit.SECONDS)) err.println("Error: UDP Proxy connections did not terminate. You may have to kill that appplication."); } } catch(InterruptedException e) { pool.shutdownNow(); } synchronized(mainLock) { mainLock.notify(); } } } /*==========================================================================*/ public class ClientConnection extends CommandNetwork implements Runnable { private final SocketChannel sock; private final FSRecords fileservers; private final UserRecords users; private final ObjectInputStream clin; private final ObjectOutputStream clout; private UserRecord user = null; private final String clientaddr; private CommandNetwork fscmd; private FSRecord fileserver = null; private SocketChannel fsschannel = null; private ObjectInputStream fsin = null; private ObjectOutputStream fsout = null; ClientConnection(SocketChannel sock, FSRecords fileservers, UserRecords users) throws NoSuchMethodException, IOException { this.sock = sock; this.clout = new ObjectOutputStream(sock.socket().getOutputStream()); this.clin = new ObjectInputStream(new BufferedInputStream(sock.socket().getInputStream())); this.fileservers = fileservers; this.users = users; this.clientaddr = "tcp:/" + sock.socket().getInetAddress() + ":" + sock.socket().getPort(); cmdHandler.register("!login", this, "cmdLogin"); cmdHandler.register("!buy", this, "cmdBuy"); cmdHandler.register("!credits", this, "cmdCredits"); cmdHandler.register("!list", this, "cmdList"); cmdHandler.register("!download", this, "cmdDownload"); cmdHandler.register("unknown", this, "cmdUnknown"); fscmd = new CommandNetwork(); fscmd.setOneCommandMode(true); fscmd.cmdHandler.register("!error", this, "cmdFSRelayOutput"); fscmd.cmdHandler.register("!output", this, "cmdFSRelayOutput"); fscmd.cmdHandler.register("!download", this, "cmdFSDownload"); fscmd.cmdHandler.register("unknown", this, "cmdFSRelayOutput"); } /*------------------------------------------------------------------------*/ public boolean checkLogin() throws IOException { if (user == null) { Utils.sendError(clout, "Not logged in"); return false; } return true; } /*------------------------------------------------------------------------*/ public void cmdLogin(String cmd, String[] args) throws IOException { if (user != null) { Utils.sendError(clout, "Already logged in"); return; } if (args.length != 2) { Utils.sendError(clout, "Invalid Syntax: !login "); return; } synchronized(users) { UserRecord record = users.get(args[0]); if (record == null || !record.pass.equals(args[1])) { Utils.sendError(clout, "Invalid username or password"); return; } user = record; user.loggedin.add(clientaddr); } Utils.sendOutput(clout, "Successfully logged in"); } /*------------------------------------------------------------------------*/ public void cmdBuy(String cmd, String[] args) throws IOException { if (!checkLogin()) return; if (args.length != 1) { Utils.sendError(clout, "Invalid Syntax: !buy "); return; } int add = 0; try { add = Integer.parseInt(args[0]); if (add <= 0) throw new NumberFormatException(""); } catch(NumberFormatException e) { Utils.sendError(clout, "Credits must be numberic and positive"); return; } synchronized(users) { if (user.credits > Integer.MAX_VALUE - add) Utils.sendError(clout, "You can't buy that much/more credits"); else { user.credits += add; Utils.sendOutput(clout, "You now have " + user.credits + " credits"); } } } /*------------------------------------------------------------------------*/ public void cmdCredits(String cmd, String[] args) throws IOException { if (!checkLogin()) return; if (args.length != 0) { Utils.sendError(clout, "Invalid Syntax: !credits"); return; } synchronized(users) { Utils.sendOutput(clout, "You have " + user.credits + " credits left"); } } /*------------------------------------------------------------------------*/ public FSRecord getFileserver() { synchronized(fileservers) { ArrayList fslist = new ArrayList(); for(FSRecord record : fileservers.values()) { if (!record.online) continue; fslist.add(record); } if (fslist.size() == 0) return null; Collections.sort(fslist); return fslist.get(0); } } /*------------------------------------------------------------------------*/ public boolean connectFileserver() throws IOException { disconnectFileserver(); fileserver = getFileserver(); if (fileserver == null) { Utils.sendError(clout, "Unable to execute command. No fileservers online"); return false; } try { fsschannel = SocketChannel.open(new InetSocketAddress(fileserver.host, fileserver.port)); fsin = new ObjectInputStream(new BufferedInputStream( fsschannel.socket().getInputStream())); fsout = new ObjectOutputStream(fsschannel.socket().getOutputStream()); } catch(IOException e) { err.println("Error: Unable to connect to fileserver: " + e.getMessage()); Utils.sendError(clout, "Unable to connect to fileserver"); synchronized(fileservers) { fileserver.online = false; out.println("Fileserver marked as offline: " + fileserver.host + ":" + fileserver.port); } disconnectFileserver(); return false; } return true; } /*------------------------------------------------------------------------*/ public void disconnectFileserver() { try { if (fsin != null) fsout.flush(); } catch(IOException e) {} try { if (fsin != null) fsin.close(); } catch(IOException e) {} try { if (fsout != null) fsout.close(); } catch(IOException e) {} try { if (fsschannel != null) fsschannel.close(); } catch(IOException e) {} fsin = null; fsout = null; fileserver = null; } /*------------------------------------------------------------------------*/ public void cmdList(String cmd, String[] args) throws IOException { if (!checkLogin()) return; if (args.length != 0) { Utils.sendError(clout, "Invalid Syntax: !list"); return; } try { if (!connectFileserver()) return; fsout.writeUTF(cmd + " " + Utils.join(Arrays.asList(args), " ")); fsout.flush(); fscmd.run(fsin); } catch(IOException e) { Utils.sendError(clout, "Connection to fileserver terminated unexpected"); } clout.flush(); disconnectFileserver(); } /*------------------------------------------------------------------------*/ public void cmdDownload(String cmd, String[] args) throws IOException { if (!checkLogin()) return; if (args.length != 1) { Utils.sendError(clout, "Invalid Syntax: !download "); return; } try { if (!connectFileserver()) return; synchronized(users) { fsout.writeUTF(cmd + " " + Utils.join(Arrays.asList(args), " ") + " " + user.credits); } fsout.flush(); fscmd.run(fsin); } catch(IOException e) { Utils.sendError(clout, "Connection to fileserver terminated unexpected"); } clout.flush(); disconnectFileserver(); } /*------------------------------------------------------------------------*/ public void cmdUnknown(String cmd, String[] args) throws IOException { err.println("Error: Unknown data from client: " + cmd + " " + Utils.join(Arrays.asList(args), " ")); Utils.sendError(clout, "Unknown command"); } /*------------------------------------------------------------------------*/ public void cmdFSRelayOutput(String cmd, String[] args) throws IOException { long num; if ((num = Utils.parseHeaderNum(args, 0)) < 0) return; String msg; clout.writeUTF(cmd + " " + Utils.join(Arrays.asList(args), " ")); for (; num > 0 && (msg = fsin.readUTF()) != null; --num) clout.writeUTF(msg); } /*------------------------------------------------------------------------*/ public void cmdFSDownload(String cmd, String[] args) throws IOException { if (args.length < 2 || args[1].length() <= 0) { err.println("Error: Invalid " + cmd + "-command paket from fileserver. Ignoring..."); Utils.sendError(clout, "Internal Error: Invalid packet from fileserver"); return; } long filesize, filesizecpy; if ((filesize = Utils.parseHeaderNum(args, 1)) < 0) return; filesizecpy = filesize; clout.writeUTF(cmd + " " + Utils.join(Arrays.asList(args), " ")); try { byte[] buffer = new byte[8 * 1024]; int toread = buffer.length; while(filesize > 0) { if (filesize < toread) toread = (int) filesize; int count = fsin.read(buffer, 0, toread); if (count == -1) throw new IOException("Connection reset by peer"); clout.write(buffer, 0, count); filesize -= count; } synchronized(users) { user.credits -= filesizecpy; } synchronized(fileservers) { fileserver.usage += filesizecpy; } } catch(IOException e) { err.println("Error during file transfer: " + e.getMessage() + ". Closing connection to client"); stop(); synchronized(fileservers) { fileserver.usage += filesizecpy; fileserver.online = false; out.println("Fileserver marked as offline: " + fileserver.host + ":" + fileserver.port); } } } /*------------------------------------------------------------------------*/ public void shutdown() { disconnectFileserver(); try { clout.flush(); } catch(IOException e) {} try { clin.close(); } catch(IOException e) {} try { clout.close(); } catch(IOException e) {} try { if (sock.isOpen()) sock.close(); } catch(IOException e) {} } /*------------------------------------------------------------------------*/ public void run() { try { out.println("[" + Thread.currentThread().getId() + "] New client connection from " + clientaddr); run(clin); clout.flush(); } catch(CommandHandler.Exception e) { err.println("Internal Error: " + e.getMessage()); e.printStackTrace(); } catch(IOException e) { /* ignore that exception * it's usually a closed connection from client so * we can't do anything about it anyway */ } if (user != null) { synchronized(users) { user.loggedin.remove(user.loggedin.indexOf(clientaddr)); } } out.println("[" + Thread.currentThread().getId() + "] Connection closed"); shutdown(); } } /*==========================================================================*/ public class TCPSocketReader implements Runnable { private final ServerSocketChannel sschannel; private final FSRecords fileservers; private final UserRecords users; private final Object mainLock; private final ExecutorService pool; TCPSocketReader(ServerSocketChannel sschannel, FSRecords fileservers, UserRecords users, Object mainLock) { this.sschannel = sschannel; this.fileservers = fileservers; this.users = users; this.mainLock = mainLock; this.pool = Executors.newCachedThreadPool(); } /*------------------------------------------------------------------------*/ public void run() { try { while(true) pool.execute(new ClientConnection(sschannel.accept(), fileservers, users)); } catch(NoSuchMethodException e) { err.println("Error: Unable to setup remote command handler"); } catch(IOException e) { /* ignore that exception * thread will shutdown and unlock the main thread * which will shutdown the application */ } pool.shutdown(); try { if (!pool.awaitTermination(100, TimeUnit.MILLISECONDS)) out.println("Trying to shutdown the client connections. This may take up to 15 seconds..."); if (!pool.awaitTermination(5, TimeUnit.SECONDS)) { pool.shutdownNow(); if (!pool.awaitTermination(5, TimeUnit.SECONDS)) err.println("Error: Client connections did not terminate. You may have to kill that appplication."); } } catch(InterruptedException e) { pool.shutdownNow(); } synchronized(mainLock) { mainLock.notify(); } } } /*==========================================================================*/ public class Interactive extends CommandInteractive implements Runnable { private final InputStream sin; private final Object mainLock; Interactive(InputStream sin, Object mainLock) throws NoSuchMethodException { this.sin = sin; this.mainLock = mainLock; cmdHandler.register("unknown", this, "cmdUnknown"); cmdHandler.register("!fileservers", this, "cmdFileservers"); cmdHandler.register("!users", this, "cmdUsers"); cmdHandler.register("!exit", this, "cmdExit"); } /*------------------------------------------------------------------------*/ public void cmdUnknown(String cmd, String[] args) { err.println("Unknown command: " + cmd + " " + Utils.join(Arrays.asList(args), " ")); } /*------------------------------------------------------------------------*/ public void cmdFileservers(String cmd, String[] args) { synchronized(fileservers) { if (fileservers.size() == 0) out.println("No fileservers registered"); else { int line = 1; int pad = Integer.toString(fileservers.size()).length(); for(Map.Entry entry : fileservers.entrySet()) { FSRecord record = entry.getValue(); out.println(String.format("%0" + pad + "d. IP: %s, Port: %d, %s, Usage: %d", line, record.host, record.port, (record.online) ? "online" : "offline", record.usage)); ++line; } } } } /*------------------------------------------------------------------------*/ public void cmdUsers(String cmd, String[] args) { synchronized(users) { if (users.size() == 0) out.println("No users registered"); else { int line = 1; int pad = Integer.toString(users.size()).length(); for(Map.Entry entry : users.entrySet()) { UserRecord record = entry.getValue(); out.println(String.format("%0" + pad + "d. User: %s, %s, Credits: %d", line, record.name, (record.loggedin.size() > 0) ? "online" : "offline", record.credits)); for(String host : record.loggedin) out.println(String.format("%1$#" + (pad + 1) + "s Client: %2$s", " ", host)); ++line; } } } } /*------------------------------------------------------------------------*/ public void cmdExit(String cmd, String[] args) { stop(); } /*------------------------------------------------------------------------*/ public void printPrompt() { out.print(">: "); out.flush(); } /*------------------------------------------------------------------------*/ public void run() { try { run(sin); } catch(CommandHandler.Exception e) { err.println("Internal Error: " + e.getMessage()); } catch (IOException e) { /* ignore that exception * thread will shutdown and unlock the main thread * which will shutdown the application */ } synchronized(mainLock) { mainLock.notify(); } } } /*==========================================================================*/ public class CheckFSTask implements Runnable { private FSRecords fileservers; private final int fserverTimeout; CheckFSTask(FSRecords fileservers, int fserverTimeout) { this.fileservers = fileservers; this.fserverTimeout = fserverTimeout; } /*------------------------------------------------------------------------*/ public void run() { synchronized(fileservers) { long curTime = Calendar.getInstance().getTimeInMillis(); for(Map.Entry entry : fileservers.entrySet()) { if (entry.getValue().online && entry.getValue().lastUpdate + fserverTimeout < curTime) { entry.getValue().online = false; out.println("Fileserver has gone offline: " + entry.getKey()); } } } } } /*==========================================================================*/ private static int tcpPort; private static int udpPort; private static int fserverTimeout; private static int checkPeriod; private FSRecords fileservers; private UserRecords users; private ScheduledExecutorService scheduler = null; private DatagramChannel dchannel = null; private Thread tUDPSocketReader = null; private ServerSocketChannel sschannel = null; private Thread tTCPSocketReader = null; private Thread tInteractive = null; private InputStream stdin = null; private final Object mainLock = new Object(); /*--------------------------------------------------------------------------*/ Proxy() { fileservers = new FSRecords(); users = new UserRecords(); } /*--------------------------------------------------------------------------*/ public static void usage() throws Utils.Shutdown { out.println("Usage: Proxy tcpPort udpPort fserverTimeout checkPeriod\n"); out.println("tcpPort\t\t...the port to be used for instantiating a ServerSocket"); out.println("udpPort\t\t...the port to be used for instantiating a DatagramSocket"); out.println("fserverTimeout\t...the period in milliseconds each fileserver has to send an isAlive packet"); out.println("\t\t if no such packet is received within this time, the fileserver is assumed"); out.println("\t\t to be offline and is no longer available for handling requests"); out.println("checkPeriod\t...specifies that the test whether a fileserver has timed-out or not"); out.println("\t\t (see fileserverTimeout). is repeated every checkPeriod milliseconds"); // Java is some piece of crap which doesn't allow me to set exitcode w/o // using System.exit. Maybe someday Java will be a fully functional // programming language, but I wouldn't bet my money //System.exit(1); throw new Utils.Shutdown("FUCK YOU JAVA"); } /*--------------------------------------------------------------------------*/ public void bailout(String error) throws Utils.Shutdown { err.println("Error: " + error); shutdown(); // Java is some piece of crap which doesn't allow me to set exitcode w/o // using System.exit. Maybe someday Java will be a fully functional // programming language, but I wouldn't bet my money //System.exit(2); throw new Utils.Shutdown("FUCK YOU JAVA"); } /*--------------------------------------------------------------------------*/ public void parseArgs(String[] args) { if (args.length != 4) usage(); try { tcpPort = Integer.parseInt(args[0]); if (tcpPort <= 0 || tcpPort > 65536) bailout("tcpPort must be a valid port number (1 - 65535)"); } catch(NumberFormatException e) { bailout("tcpPort must be numeric"); } try { udpPort = Integer.parseInt(args[1]); if (udpPort <= 0 || udpPort > 65536) bailout("udpPort must be a valid port number (1 - 65535)"); } catch(NumberFormatException e) { bailout("udpPort must be numeric"); } try { fserverTimeout = Integer.parseInt(args[2]); if (fserverTimeout <= 0) bailout("fserverTimeout must be positive"); } catch(NumberFormatException e) { bailout("fserverTimeout must be numeric"); } try { checkPeriod = Integer.parseInt(args[3]); if (checkPeriod <= 0) bailout("checkPeriod must be positive"); } catch(NumberFormatException e) { bailout("checkPeriod must be numeric"); } } /*--------------------------------------------------------------------------*/ public void parseUsers(InputStream in) throws IOException, IllegalArgumentException { if (in == null) throw new IOException("Properties file doesn't exist"); Properties props = new Properties(); props.load(in); for(String prop : props.stringPropertyNames()) { String[] pieces = prop.split("\\.", 2); String user = pieces[0]; if (pieces.length == 1) users.put(user, new UserRecord(user, props.getProperty(prop))); else if (pieces.length == 2) { UserRecord record = users.get(user); if (record == null) { err.println("Can't load user properties for unknown user '" + user + "'. Skipping..."); continue; } if (pieces[1].equals("credits")) { try { int credits = Integer.parseInt(props.getProperty(prop)); if (credits < 0) { err.println("Property " + prop + " must be positive number. Skipping..."); continue; } record.credits = credits; } catch(NumberFormatException e) { err.println("Property " + prop + " must be numeric. Skipping..."); } } else err.println("Property " + prop + " is unknown. Skipping..."); } else err.println("Property " + prop + " is unknown. Skipping..."); } } /*--------------------------------------------------------------------------*/ public void shutdown() { try { if (scheduler != null) { scheduler.shutdownNow(); scheduler.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } } catch(InterruptedException e) {} try { if (dchannel != null) dchannel.close(); } catch(IOException e) {} try { if (tUDPSocketReader != null) tUDPSocketReader.join(); } catch(InterruptedException e) {} try { if (sschannel != null) sschannel.close(); } catch(IOException e) {} try { if (tTCPSocketReader != null) tTCPSocketReader.join(); } catch(InterruptedException e) {} try { if (tInteractive != null) { tInteractive.interrupt(); tInteractive.join(); } } catch(InterruptedException e) {} try { if (stdin != null) stdin.close(); } catch(IOException e) {} } /*--------------------------------------------------------------------------*/ public void run(String[] args) { parseArgs(args); synchronized(users) { try { InputStream in = ClassLoader.getSystemResourceAsStream("user.properties"); if (in == null) bailout("Properties file doesn't exist or isn't readable"); parseUsers(in); in.close(); out.println("Users loaded successfully"); } catch(IOException e) { bailout("Unable to read from properties file: " + e.getMessage()); } catch(IllegalArgumentException e) { bailout("Malformed properties file: " + e.getMessage()); } } synchronized(mainLock) { scheduler = Executors.newScheduledThreadPool(1); ScheduledFuture checkFSTimer = scheduler.scheduleAtFixedRate( new CheckFSTask(fileservers, fserverTimeout), 0, checkPeriod, TimeUnit.MILLISECONDS); try { dchannel = DatagramChannel.open(); dchannel.socket().bind(new InetSocketAddress(udpPort)); tUDPSocketReader = new Thread(new UDPSocketReader(dchannel, fileservers, mainLock)); tUDPSocketReader.start(); out.println("Listening on udp:/" + dchannel.socket().getLocalSocketAddress()); } catch(IOException e) { bailout("Unable to create UDP Socket: " + e.getMessage()); } try { sschannel = ServerSocketChannel.open(); sschannel.socket().bind(new InetSocketAddress(tcpPort)); tTCPSocketReader = new Thread(new TCPSocketReader(sschannel, fileservers, users, mainLock)); tTCPSocketReader.start(); out.println("Listening on tcp:/" + sschannel.socket().getLocalSocketAddress()); } catch(IOException e) { bailout("Unable to create TCP Socket: " + e.getMessage()); } try { InputStream stdin = java.nio.channels.Channels.newInputStream( new FileInputStream(FileDescriptor.in).getChannel()); tInteractive = new Thread(new Interactive(stdin, mainLock)); tInteractive.start(); } catch(NoSuchMethodException e) { bailout("Unable to setup interactive command handler"); } out.println("Proxy startup successful!"); try { mainLock.wait(); } catch(InterruptedException e) { /* if we get interrupted -> ignore */ } try { /* let the threads shutdown */ Thread.sleep(100); } catch(InterruptedException e) {} } if (tUDPSocketReader != null && !tUDPSocketReader.isAlive()) bailout("Listening UDP socket closed unexpected. Terminating..."); if (tTCPSocketReader != null && !tTCPSocketReader.isAlive()) bailout("Listening TCP socket closed unexpected. Terminating..."); shutdown(); } /*--------------------------------------------------------------------------*/ public static void main(String[] args) { try { Proxy proxy = new Proxy(); proxy.run(args); } catch(Utils.Shutdown e) {} } }