Download | Plain Text | No 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 java.nio.ByteBuffer;
- import java.nio.channels.ServerSocketChannel;
- import java.nio.channels.SocketChannel;
-
- 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.Arrays;
-
- import java.net.*;
- import java.io.*;
-
- /*
- * Fileserver 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 Fileserver
- {
- public class ProxyConnection
- extends CommandNetwork
- implements Runnable
- {
- private final SocketChannel sock;
-
- {
- this.sock = sock;
- this.sharedFilesDir = sharedFilesDir;
-
- setOneCommandMode(true);
- cmdHandler.register("!list", this, "cmdList");
- cmdHandler.register("!download", this, "cmdDownload");
- cmdHandler.register("unknown", this, "cmdUnknown");
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- if (args.length != 0)
- {
- err.println("Error: Invalid " + cmd + "-command paket from proxy. Ignoring...");
- return;
- }
-
- {
- {
- return (file.isFile() && file.canRead());
- }
- };
- Utils.sendOutput(oout, file.list(filter));
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- if (args.length < 2 || args[1].length() <= 0)
- {
- err.println("Error: Invalid " + cmd + "-command paket from proxy. Ignoring...");
- return;
- }
-
- long credits;
- if ((credits = Utils.parseHeaderNum(args, 1)) < 0)
- return;
-
- if (!file.exists() || !file.getParent().equals(sharedFilesDir))
- {
- Utils.sendError(oout, "File '" + args[0] + "' does not exist");
- return;
- }
- if (!file.isFile())
- {
- Utils.sendError(oout, "File '" + args[0] + "' is not a file");
- return;
- }
- if (!file.canRead())
- {
- Utils.sendError(oout, "File '" + args[0] + "' is not readable");
- return;
- }
-
- long filesize = file.length();
- if (credits < filesize)
- {
- Utils.sendOutput(oout, "You don't have enough credits");
- return;
- }
-
- try
- {
-
- oout.writeUTF(cmd + " " + file.getName() + " " + filesize);
-
- byte[] buffer = new byte[8 * 1024];
- int toread = buffer.length;
- while(filesize > 0)
- {
- if (filesize < toread)
- toread = (int) filesize;
- int count = fin.read(buffer, 0, toread);
- if (count == -1)
- oout.write(buffer, 0, count);
- filesize -= count;
- }
- }
- {
- Utils.sendError(oout, "File '" + args[0] + "' is not readable");
- }
- {
- err.println("Error during file transfer: " + e.getMessage());
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- err.println("Error: Unknown data from proxy: " + cmd + " "
- Utils.sendError(oout, "Unknown command");
- }
-
- /*------------------------------------------------------------------------*/
-
- public void shutdown()
- {
- try
- {
- oout.flush();
- }
- {}
-
- try
- {
- oin.close();
- }
- {}
-
- try
- {
- oout.close();
- }
- {}
-
- try
- {
- if (sock.isOpen())
- sock.close();
- }
- {}
- }
-
- /*------------------------------------------------------------------------*/
-
- public void run()
- {
- try
- {
- + sock.socket().getInetAddress() + ":" + sock.socket().getPort());
- run(oin);
- oout.flush();
- }
- {
- err.println("Internal Error: " + e.getMessage());
- }
- {
- /* ignore that exception
- * it's usually a closed connection from client so
- * we can't do anything about it anyway
- */
- }
-
- shutdown();
- }
- }
-
- /*==========================================================================*/
-
- public class TCPSocketReader
- implements Runnable
- {
- private final ServerSocketChannel sschannel;
- private final ExecutorService pool;
-
- TCPSocketReader(ServerSocketChannel sschannel, String sharedFilesDir,
- Object mainLock)
- {
- this.sschannel = sschannel;
- this.sharedFilesDir = sharedFilesDir;
- this.mainLock = mainLock;
- this.pool = Executors.newCachedThreadPool();
- }
-
- /*------------------------------------------------------------------------*/
-
- public void run()
- {
- try
- {
- while(true)
- pool.execute(new ProxyConnection(sschannel.accept(), sharedFilesDir));
- }
- {
- err.println("Error: Unable to setup remote command handler");
- }
- {
- /* 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 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: Proxy connections did not terminate. You may have to kill that appplication.");
- }
- }
- {
- pool.shutdownNow();
- }
-
- synchronized(mainLock)
- {
- mainLock.notify();
- }
- }
- }
-
- /*==========================================================================*/
-
- public class Interactive
- extends CommandInteractive
- implements Runnable
- {
-
- throws NoSuchMethodException
- {
- this.sin = sin;
- this.mainLock = mainLock;
-
- cmdHandler.register("unknown", this, "cmdUnknown");
- cmdHandler.register("!exit", this, "cmdExit");
- }
-
- /*------------------------------------------------------------------------*/
-
- {
- err.println("Unknown command: " + cmd + " "
- }
-
- /*------------------------------------------------------------------------*/
-
- {
- stop();
- }
-
- /*------------------------------------------------------------------------*/
-
- public void printPrompt()
- {
- out.print(">: ");
- out.flush();
- }
-
- /*------------------------------------------------------------------------*/
-
- public void run()
- {
- try
- {
- run(sin);
- }
- {
- err.println("Internal Error: " + e.getMessage());
- }
- {
- /* ignore that exception
- * thread will shutdown and unlock the main thread
- * which will shutdown the application
- */
- }
-
- synchronized(mainLock)
- {
- mainLock.notify();
- }
- }
- }
-
- /*==========================================================================*/
-
- public class PingTask
- implements Runnable
- {
-
- {
- this.sock = sock;
- this.packet = packet;
- this.mainLock = mainLock;
- }
-
- /*------------------------------------------------------------------------*/
-
- public void run()
- {
- try
- {
- sock.send(packet);
- }
- {
- err.println("Error while sending UDP ping packet: " + e.getMessage()
- + ". Terminating...");
- synchronized(mainLock)
- {
- mainLock.notify();
- }
- }
- }
- }
-
- /*==========================================================================*/
-
- private static int tcpPort;
- private static int proxyUDPPort;
- private static int alivePeriod;
- private ScheduledExecutorService scheduler = null;
- private ServerSocketChannel sschannel = null;
-
- /*--------------------------------------------------------------------------*/
-
- public static void usage()
- throws Utils.Shutdown
- {
- out.println("Usage: Fileserver sharedFilesDir tcpPort proxyHost proxyUDPPort alivePeriod\n");
- out.println("sharedFilesDir\t...the directory that contains all the files clients can download");
- out.println("tcpPort\t\t...the port to be used for instantiating a ServerSocket");
- out.println("proxyHost\t...the host name or an IP address where the Proxy is running");
- out.println("proxyUDPPort\t...the UDP port where the Proxy is listening for fileserver datagrams");
- out.println("alivePeriod\t...the period in ms the fileserver needs to send an isAlive datagram to the Proxy");
-
- // 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");
- }
-
- /*--------------------------------------------------------------------------*/
-
- 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");
- }
-
- /*--------------------------------------------------------------------------*/
-
- {
- if (args.length != 5)
- usage();
-
- sharedFilesDir = args[0];
- if (!sharedir.isDirectory())
- bailout("sharedFilesDir '" + sharedFilesDir + "' is not a directory");
- if (!sharedir.canRead())
- bailout("sharedFilesDir '" + sharedFilesDir + "' is not readable");
-
- try
- {
- if (tcpPort <= 0 || tcpPort > 65536)
- bailout("tcpPort must be a valid port number (1 - 65535)");
- }
- {
- bailout("tcpPort must be numeric");
- }
-
- proxyHost = args[2];
- if (proxyHost.length() == 0)
- bailout("proxyHost is empty");
-
- try
- {
- if (proxyUDPPort <= 0 || proxyUDPPort > 65536)
- bailout("proxyUDPPort must be a valid port number (1 - 65535)");
- }
- {
- bailout("proxyUDPPort must be numeric");
- }
-
- try
- {
- if (alivePeriod <= 0)
- bailout("alivePeriod must be positive");
- }
- {
- bailout("alivePeriod must be numeric");
- }
- }
-
- /*--------------------------------------------------------------------------*/
-
- public void shutdown()
- {
- try
- {
- if (scheduler != null)
- {
- scheduler.shutdownNow();
- }
- }
- {}
-
- if (dsock != null)
- dsock.close();
-
- try
- {
- if (sschannel != null)
- sschannel.close();
- }
- {}
-
- try
- {
- if (tTCPSocketReader != null)
- tTCPSocketReader.join();
- }
- {}
-
- try
- {
- if (tInteractive != null)
- {
- tInteractive.interrupt();
- tInteractive.join();
- }
- }
- {}
-
- try
- {
- if (stdin != null)
- stdin.close();
- }
- {}
- }
-
- /*--------------------------------------------------------------------------*/
-
- {
- parseArgs(args);
-
- synchronized(mainLock)
- {
- try
- {
- ByteBuffer buffer = ByteBuffer.allocate(4);
- buffer.putInt(tcpPort);
- buffer.array().length, proxyaddr, proxyUDPPort);
-
- scheduler = Executors.newScheduledThreadPool(1);
- ScheduledFuture<?> pingTimer = scheduler.scheduleAtFixedRate(
- new PingTask(dsock, dpacket, mainLock),
- 0, alivePeriod, TimeUnit.MILLISECONDS);
- }
- {
- bailout("Unable to create UDP Socket: " + e.getMessage());
- }
- {
- bailout("Unable to resolve hostname: " + e.getMessage());
- }
-
- try
- {
- sschannel = ServerSocketChannel.open();
- sschannel.socket().bind(new InetSocketAddress(tcpPort));
- sharedFilesDir, mainLock));
- tTCPSocketReader.start();
- out.println("Listening on tcp:/" + sschannel.socket().getLocalSocketAddress());
- }
- {
- bailout("Unable to create TCP Socket: " + e.getMessage());
- }
-
- try
- {
- tInteractive.start();
- }
- {
- bailout("Unable to setup interactive command handler");
- }
-
- out.println("Fileserver startup successful!");
- try
- {
- mainLock.wait();
- }
- {
- /* if we get interrupted -> ignore */
- }
-
- try
- {
- /* let the threads shutdown */
- }
- {}
- }
-
- if (tTCPSocketReader != null && !tTCPSocketReader.isAlive())
- bailout("Listening TCP socket closed unexpected. Terminating...");
-
- shutdown();
- }
-
- /*--------------------------------------------------------------------------*/
-
- {
- try
- {
- Fileserver fserver = new Fileserver();
- fserver.run(args);
- }
- catch(Utils.Shutdown e)
- {}
- }
- }
-