Download | Plain Text | Line Numbers
/*
* 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<FSRecord>
{
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<String, FSRecord>
{}
/*==========================================================================*/
public class UserRecord
{
public final String name;
public final String pass;
public int credits;
public ArrayList<String> loggedin;
UserRecord(String name, String pass)
{
this.name = name;
this.pass = pass;
credits = 0;
loggedin = new ArrayList<String>();
}
}
public class UserRecords
extends HashMap<String, UserRecord>
{}
/*==========================================================================*/
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 <username> <password>");
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 <credits>");
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<FSRecord> fslist = new ArrayList<FSRecord>();
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 <filename>");
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<String, FSRecord> 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<String, UserRecord> 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<String, FSRecord> 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)
{}
}
}