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.text.SimpleDateFormat;
import java.text.ParseException;
import java.rmi.registry.*;
import java.rmi.server.*;
import java.rmi.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.Collections;
import java.util.Properties;
import java.util.HashSet;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.io.*;
/*
* Client implementation for Lab#2 of DSLab WS10
* See angabe.pdf for details
*
* This code is not documented at all. This is volitional
*
* @author Manuel Mausz (0728348)
*/
public class Client
{
public class WaitForServer
implements Runnable
{
public void shutdown()
{
scheduler.shutdown();
synchronized(mainLock)
{
mainLock.notify();
}
}
public void run()
{
synchronized(registry)
{
try
{
int i;
String[] tmp = registry.list();
if ((i = Arrays.asList(tmp).indexOf(serverName)) != -1)
{
c2siface = ((RemoteInterface)registry.lookup(Arrays.asList(tmp).get(i))).c2sInterfaceFactory();
if (c2siface != null)
shutdown();
}
}
catch(NotBoundException e)
{
err.println("Error: " + e.getMessage()); /* this shouldn't happen */
shutdown();
}
catch(RemoteException e)
{
err.println("Unable to lookup servers: " + e.getMessage());
shutdown();
}
}
}
}
/*==========================================================================*/
public class Interactive
extends CommandInteractive
implements Runnable
{
private final InputStream sin;
private final Object mainLock;
private boolean loggedin = false;
Interactive(InputStream sin, Object mainLock)
throws NoSuchMethodException, IOException
{
this.sin = sin;
this.mainLock = mainLock;
cmdHandler.register("!register", this, "cmdRegister");
cmdHandler.register("!login", this, "cmdLogin");
cmdHandler.register("!create", this, "cmdCreate");
cmdHandler.register("!addDate", this, "cmdAddDate");
cmdHandler.register("!invite", this, "cmdInvite");
cmdHandler.register("!get", this, "cmdGet");
cmdHandler.register("!vote", this, "cmdVote");
cmdHandler.register("!finalize", this, "cmdFinalize");
cmdHandler.register("!logout", this, "cmdLogout");
cmdHandler.register("!exit", this, "cmdExit");
cmdHandler.register("!help", this, "cmdHelp");
cmdHandler.register("unknown", this, "cmdUnknown");
}
/*------------------------------------------------------------------------*/
public void cmdRegister(String cmd, String[] args)
{
if (args.length != 2)
{
err.println("Invalid Syntax: " + cmd + " <username> <password>");
return;
}
try
{
if (c2siface.register(args[0], args[1]))
out.println("Successfully registered.");
else
err.println("Username already registered.");
}
catch(RemoteException e)
{
err.println("Error: " + e.getCause().getMessage());
}
}
/*------------------------------------------------------------------------*/
public void cmdLogin(String cmd, String[] args)
{
if (args.length != 2)
{
err.println("Invalid Syntax: " + cmd + " <username> <password>");
return;
}
try
{
if (c2siface.login(args[0], args[1], s2ciface))
{
out.println("Successfully logged in.");
loggedin = true;
}
else
err.println("Wrong username or password.");
}
catch(RemoteException e)
{
err.println("Error: " + e.getCause().getMessage());
}
}
/*------------------------------------------------------------------------*/
public void cmdCreate(String cmd, String[] args)
{
if (args.length != 3)
{
err.println("Invalid Syntax: " + cmd + " <name> <location> <duration in minutes>");
return;
}
int duration = 0;
try
{
duration = Integer.parseInt(args[2]);
if (duration <= 0)
{
err.println("Error: Invalid argument: Duration must be a positive number.");
return;
}
}
catch(NumberFormatException e)
{
err.println("Error: Invalid argument: Duration must be numeric.");
return;
}
try
{
if (c2siface.create(args[0], args[1], duration))
out.println("Event created successfully.");
else
err.println("Unable to create Event. Event already exists.");
}
catch(RemoteException e)
{
err.println("Error: " + e.getCause().getMessage());
}
}
/*------------------------------------------------------------------------*/
public void cmdAddDate(String cmd, String[] args)
{
SimpleDateFormat datefmt = new SimpleDateFormat("dd.MM.yyyy/HH:mm");
if (args.length != 2)
{
err.println("Invalid Syntax: " + cmd + " <name of event> <date: " + datefmt.toPattern() + ">");
return;
}
Date date = null;
try
{
date = datefmt.parse(args[1]);
}
catch(ParseException e)
{}
if (date == null)
{
err.println("Error: Invalid date argument. Syntax: <" + datefmt.toPattern() + ">.");
return;
}
try
{
c2siface.addDate(args[0], date);
out.println("Date option added.");
}
catch(RemoteException e)
{
err.println("Error: Unable to add date: " + e.getCause().getMessage());
}
}
/*------------------------------------------------------------------------*/
public void cmdInvite(String cmd, String[] args)
{
if (args.length != 2)
{
err.println("Invalid Syntax: " + cmd + " <name of event> <username>");
return;
}
try
{
c2siface.invite(args[0], args[1]);
out.println("User invited.");
}
catch(RemoteException e)
{
err.println("Error: Unable to invite user: " + e.getCause().getMessage());
}
}
/*------------------------------------------------------------------------*/
public void cmdGet(String cmd, String[] args)
{
if (args.length != 1)
{
err.println("Invalid Syntax: " + cmd + " <name of event>");
return;
}
try
{
out.print(c2siface.event2String(args[0]));
out.flush();
}
catch(RemoteException e)
{
err.println("Error: Unable to fetch event: " + e.getCause().getMessage());
}
}
/*------------------------------------------------------------------------*/
public void cmdVote(String cmd, String[] args)
{
SimpleDateFormat datefmt = new SimpleDateFormat("dd.MM.yyyy/HH:mm");
if (args.length < 2)
{
err.println("Invalid Syntax: " + cmd + " <name of event> <date: " + datefmt.toPattern() + "> ...");
return;
}
int i = 1;
HashSet<Date> dates = new HashSet<Date>();
try
{
for(i = 1; i < args.length; ++i)
dates.add(datefmt.parse(args[i]));
}
catch(ParseException e)
{
err.println("Error: Invalid date \"" + args[i] + "\". Syntax: <" + datefmt.toPattern() + ">.");
return;
}
try
{
c2siface.vote(args[0], dates.toArray(new Date[dates.size()]));
out.println("Vote registered.");
}
catch(RemoteException e)
{
err.println("Error: Unable to vote: " + e.getCause().getMessage());
}
}
/*------------------------------------------------------------------------*/
public void cmdFinalize(String cmd, String[] args)
{
if (args.length != 1)
{
err.println("Invalid Syntax: " + cmd + " <name of event>");
return;
}
try
{
c2siface.finalize(args[0]);
}
catch(RemoteException e)
{
err.println("Error: Unable to finalize: " + e.getCause().getMessage());
}
}
/*------------------------------------------------------------------------*/
public void cmdLogout(String cmd, String[] args)
{
if (args.length != 0)
{
err.println("Invalid Syntax: " + cmd);
return;
}
try
{
c2siface.logout();
loggedin = false;
out.println("You have been logged out.");
}
catch(RemoteException e)
{
err.println("Error: " + e.getCause().getMessage());
}
}
/*------------------------------------------------------------------------*/
public void cmdExit(String cmd, String[] args)
{
if (loggedin)
cmdLogout("!logout", new String[0]);
stop();
}
/*------------------------------------------------------------------------*/
public void cmdHelp(String cmd, String[] args)
throws IOException
{
for(Map.Entry<String, Object[]> entry : cmdHandler.entrySet())
{
if (entry.getKey().equals("unknown") || entry.getKey().equals("!help"))
continue;
out.println(entry.getKey());
}
}
/*------------------------------------------------------------------------*/
public void cmdUnknown(String cmd, String[] args)
throws IOException
{
out.println("Error: Unknown command: " + cmd + " " + Utils.join(Arrays.asList(args), " "));
out.println("Try !help for a list of commands");
}
/*------------------------------------------------------------------------*/
public void printPrompt()
{
out.print(">: ");
out.flush();
}
/*------------------------------------------------------------------------*/
public void shutdown()
{}
/*------------------------------------------------------------------------*/
public void run()
{
try
{
run(sin);
}
catch(RemoteException e)
{
err.println("Remote Error: " + e.getMessage());
}
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
*/
}
shutdown();
synchronized(mainLock)
{
mainLock.notify();
}
}
}
/*==========================================================================*/
private static String serverName;
private InputStream stdin = null;
private Thread tInteractive = null;
private ScheduledExecutorService scheduler = null;
private Registry registry = null;
private C2SInterface c2siface = null;
private S2CInterfaceImpl s2ciface = null;
private final Object mainLock = new Object();
/*--------------------------------------------------------------------------*/
public static void usage()
throws Utils.Shutdown
{
out.println("Usage: Server serverName\n");
out.println("serverName\t...the name of the remote reference in the RMI registry");
out.println("\t\t of the server that shall be responsible for this client.");
// 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
{
if (error != null)
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 != 1)
usage();
serverName = args[0];
if (serverName.length() == 0)
bailout("serverName is empty");
}
/*--------------------------------------------------------------------------*/
public void createInterface()
{
String regHost = null;
int regPort = 0;
try
{
InputStream in = ClassLoader.getSystemResourceAsStream("registry.properties");
if (in == null)
bailout("Properties file doesn't exist or isn't readable");
Properties props = new Properties();
props.load(in);
for(String prop : props.stringPropertyNames())
{
if (prop.equals("registry.host"))
regHost = props.getProperty(prop);
else if (prop.equals("registry.port"))
{
try
{
regPort = Integer.parseInt(props.getProperty(prop));
if (regPort <= 0 || regPort > 65536)
{
err.println("Property " + prop + " must be a valid port number (1 - 65535). Skipping...");
regPort = 0;
}
}
catch(NumberFormatException e)
{
err.println("Property " + prop + " must be numeric. Skipping...");
}
}
else
err.println("Property " + prop + " is unknown. Skipping...");
}
in.close();
}
catch(IOException e)
{
bailout("Unable to read from properties file: " + e.getMessage());
}
if (regHost == null || regHost.length() <= 0)
bailout("Registry host is not set");
if (regPort == 0)
bailout("Registry port is not set");
try
{
registry = LocateRegistry.getRegistry(regHost, regPort);
}
catch(RemoteException e)
{
bailout("Unable to get registry: " + e.getMessage());
}
}
/*--------------------------------------------------------------------------*/
public void remoteLogout()
{
if (tInteractive != null)
tInteractive.interrupt();
}
/*--------------------------------------------------------------------------*/
public void shutdown()
{
try
{
if (scheduler != null)
{
scheduler.shutdownNow();
scheduler.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
}
}
catch(InterruptedException e)
{}
try
{
if (s2ciface != null)
s2ciface.unexport();
}
catch(RemoteException 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(mainLock)
{
createInterface();
try
{
out.println("Waiting for the server to come online...");
scheduler = Executors.newScheduledThreadPool(1);
ScheduledFuture<?> waitServerTimer = scheduler.scheduleAtFixedRate(
new WaitForServer(), 0, 500, TimeUnit.MILLISECONDS);
mainLock.wait();
if (!scheduler.isShutdown() || c2siface == null)
bailout(null);
}
catch(InterruptedException e)
{
/* if we get interrupted -> ignore */
}
try
{
s2ciface = new S2CInterfaceImpl(this);
}
catch(RemoteException e)
{
bailout("Unable to export client callback");
}
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");
}
catch(IOException e)
{
bailout("Unable to create object output stream: " + e.getMessage());
}
out.println("Client startup successful!");
try
{
mainLock.wait();
}
catch(InterruptedException e)
{
/* if we get interrupted -> ignore */
}
}
shutdown();
}
/*--------------------------------------------------------------------------*/
public static void main(String[] args)
{
try
{
Client cli = new Client();
cli.run(args);
}
catch(Utils.Shutdown e)
{}
}
}