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.util.concurrent.ScheduledExecutorService;
- import java.util.concurrent.ScheduledFuture;
- import java.util.concurrent.Executors;
- import java.util.concurrent.TimeUnit;
-
- import java.rmi.registry.*;
- import java.rmi.server.*;
- import java.rmi.*;
-
- import java.text.SimpleDateFormat;
- import java.lang.StringBuffer;
- import java.util.Collections;
- import java.util.Comparator;
- import java.util.Properties;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Arrays;
- import java.util.Date;
- import java.util.Set;
- import java.util.Map;
-
- import java.io.*;
-
- /*
- * Server 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 Server
- implements Serializable
- {
- public class ServerRecords
- {}
-
- /*==========================================================================*/
-
- public class UserRecord
- implements Serializable
- {
- public boolean committed = false;
- public S2CInterface s2ciface = null;
-
- {
- this.name = name;
- this.server = server;
- }
-
- /*------------------------------------------------------------------------*/
-
- public synchronized boolean isCommitted()
- {
- return committed;
- }
-
- /*------------------------------------------------------------------------*/
-
- public synchronized void commit()
- {
- committed = true;
- }
- }
-
- /*==========================================================================*/
-
- public class UserRecords
- {
- {
- if (!registerPrepare(user, bindingName))
- return false;
-
- synchronized(servers)
- {
- ArrayList<String> rollbacklist = new ArrayList<String>();
- boolean ret = true;
-
- {
- try
- {
- ret = servers.get(server).registerPrepare(user, bindingName);
- }
- {
- ret = false;
- }
-
- if (!ret)
- break;
- rollbacklist.add(server);
- }
-
- if (!ret)
- {
- {
- try
- {
- servers.get(server).registerRollback(user);
- }
- {}
- }
- registerRollback(user);
- return false;
- }
-
- {
- try
- {
- servers.get(server).registerCommit(user);
- }
- {}
- }
-
- registerCommit(user);
- get(user).pass = pass;
- }
-
- return true;
- }
-
- /*------------------------------------------------------------------------*/
-
- {
- if (containsKey(user))
- return false;
- put(user, new UserRecord(user, server));
- return true;
- }
-
- /*------------------------------------------------------------------------*/
-
- {
- if (!containsKey(user))
- return false;
- get(user).commit();
- return true;
- }
-
- /*------------------------------------------------------------------------*/
-
- {
- out.println("rollback");
- if (!containsKey(user))
- return true;
- if (get(user).isCommitted())
- return false;
- remove(user);
- return true;
- }
-
- /*------------------------------------------------------------------------*/
-
- {
- UserRecord record = get(user);
- if (record == null)
- return null;
-
- synchronized(record)
- {
- if (record.pass == null || !record.pass.equals(pass) || !record.isCommitted())
- return null;
-
- try
- {
- if (record.s2ciface != null)
- {
- record.s2ciface.notify("Another user logged in using your credentials. You will get logged out now.");
- record.s2ciface.logout();
- }
- }
- {}
-
- record.s2ciface = s2ciface;
- }
- return record;
- }
-
- /*------------------------------------------------------------------------*/
-
- public synchronized void logout(UserRecord user)
- {
- if (user != null)
- {
- synchronized(user)
- {
- user.s2ciface = null;
- }
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- throws RemoteException
- {
- UserRecord record = get(user);
- if (record == null)
- synchronized(record)
- {
- if (!record.isCommitted())
-
- if (!record.server.equals(bindingName))
- {
- S2SInterface tmp = servers.get(record.server);
- if (tmp != null)
- {
- try
- {
- tmp.notifyUser(user, msg);
- }
- {}
- }
- return;
- }
-
- if (record.s2ciface != null)
- record.s2ciface.notify(msg);
- }
- }
- }
-
- /*==========================================================================*/
-
- public class EventRecord
- implements Serializable
- {
- public int duration;
- public boolean committed = false;
- public HashSet<String> invitees;
- public HashSet<String> voted;
-
- {
- this.name = name;
- this.server = server;
- invitees = new HashSet<String>();
- voted = new HashSet<String>();
- }
-
- /*------------------------------------------------------------------------*/
-
- public synchronized boolean isCommitted()
- {
- return committed;
- }
-
- /*------------------------------------------------------------------------*/
-
- public synchronized void commit()
- {
- committed = true;
- }
- }
-
- /*==========================================================================*/
-
- public class EventRecords
- {
-
- {
- if (!createPrepare(event, bindingName))
- return false;
-
- synchronized(servers)
- {
- ArrayList<String> rollbacklist = new ArrayList<String>();
- boolean ret = true;
-
- {
- try
- {
- ret = servers.get(server).createPrepare(event, bindingName);
- }
- {
- ret = false;
- }
-
- if (!ret)
- break;
- rollbacklist.add(server);
- }
-
- if (!ret)
- {
- {
- try
- {
- servers.get(server).createRollback(event);
- }
- {}
- }
- createRollback(event);
- return false;
- }
-
- {
- try
- {
- servers.get(server).createCommit(event);
- }
- {}
- }
-
- createCommit(event);
- EventRecord record = get(event);
- synchronized(record)
- {
- record.location = location;
- record.duration = duration;
- synchronized(user)
- {
- record.author = user.name;
- }
- }
- }
-
- return true;
- }
-
- /*------------------------------------------------------------------------*/
-
- {
- if (containsKey(event))
- return false;
- put(event, new EventRecord(event, server));
- return true;
- }
-
- /*------------------------------------------------------------------------*/
-
- {
- if (!containsKey(event))
- return false;
- get(event).commit();
- return true;
- }
-
- /*------------------------------------------------------------------------*/
-
- {
- if (!containsKey(event))
- return true;
- if (get(event).isCommitted())
- return false;
- remove(event);
- return true;
- }
-
- /*------------------------------------------------------------------------*/
-
- throws RemoteException
- {
- EventRecord record = get(event);
- if (record == null)
- synchronized(record)
- {
- if (!record.isCommitted())
- synchronized(user)
- {
- if (record.author == null || !record.author.equals(user.name))
- }
- if (record.date != null)
- if (record.dates.containsKey(date))
- record.dates.put(date, 0);
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- throws RemoteException
- {
- EventRecord record = get(event);
- if (record == null)
- synchronized(record)
- {
- if (!record.isCommitted())
- synchronized(user)
- {
- if (record.author == null || !record.author.equals(user.name))
- }
- if (record.author.equals(invitee))
- if (record.date != null)
- if (record.invitees.contains(invitee))
-
- users.notify(invitee, "You have been invited to event \"" + event + "\"");
- record.invitees.add(invitee);
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- throws RemoteException
- {
- EventRecord record = get(event);
- if (record == null)
-
- synchronized(record)
- {
- if (!record.isCommitted())
- if (!record.server.equals(bindingName))
- {
- S2SInterface tmp = servers.get(record.server);
- if (tmp == null)
- return tmp.eventToString(event);
- }
-
- sb.append("Event: ").append(record.name).append("\n");
- sb.append("Location: ").append(record.location).append("\n");
- sb.append("Duration: ").append(record.duration).append(" min.\n");
- sb.append("Author: ").append(record.author).append("\n");
-
- if (record.date != null)
- sb.append("Date: ").append(datefmt.format(record.date)).append("\n");
- else
- {
- if (record.dates.size() == 0)
- sb.append("Options: None set\n");
- else
- {
- sb.append("Options:\n");
- sb.append(" * ").append(datefmt.format(date)).append(" ... ")
- .append(record.dates.get(date)).append(" votes\n");
- }
- }
-
- if (record.invitees.size() == 0)
- sb.append("Invitees: None set\n");
- else
- sb.append("Invitees: ").append(Utils.join(record.invitees, ", ")).append("\n");
-
- return sb.toString();
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- throws RemoteException
- {
- EventRecord record = get(event);
- if (record == null)
-
- synchronized(record)
- {
- if (!record.isCommitted())
- if (!record.server.equals(bindingName))
- {
- S2SInterface tmp = servers.get(record.server);
- if (tmp == null)
- try
- {
- tmp.vote(event, user, dates);
- }
- {
- }
- return;
- }
-
- if (record.date != null)
- if (record.voted.contains(user))
- if ((record.author == null || !record.author.equals(user))
- && !record.invitees.contains(user))
-
- {
- if (!recorddates.contains(date))
- }
-
- record.dates.put(date, record.dates.get(date) + 1);
- record.voted.add(user);
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- throws RemoteException
- {
- EventRecord record = get(event);
- if (record == null)
- synchronized(record)
- {
- synchronized(user)
- {
- if (!record.isCommitted())
- if (record.author == null || !record.author.equals(user.name))
- if (record.date != null)
-
- {
- return o1.getValue().compareTo(o2.getValue());
- }
- }).getKey();
-
- + datefmt.format(record.date) + ".";
- users.notify(user.name, text);
- users.notify(invitee, text);
- }
- }
- }
- }
-
- /*==========================================================================*/
-
- public class WaitForServers
- implements Runnable
- {
- private ArrayList<String> serverlist;
- WaitForServers()
- {
- serverlist = new ArrayList<String>();
- serverlist.add(bindingName);
- }
-
- /*------------------------------------------------------------------------*/
-
- public void shutdown()
- {
- scheduler.shutdown();
- synchronized(mainLock)
- {
- mainLock.notify();
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- public void run()
- {
- synchronized(registry)
- {
- try
- {
- {
- synchronized(servers)
- {
- {
- if (server.equals(bindingName))
- continue;
- servers.put(server, ((RemoteInterface)registry.lookup(server)).s2sInterfaceFactory());
- }
- }
- shutdown();
- }
- }
- {
- err.println("Error: " + e.getMessage()); /* this shouldn't happen */
- shutdown();
- }
- {
- err.println("Unable to lookup servers: " + e.getMessage());
- shutdown();
- }
- }
- }
- }
-
- /*==========================================================================*/
-
- public class Interactive
- extends CommandInteractive
- implements Runnable
- {
-
- {
- this.sin = sin;
- this.mainLock = mainLock;
-
- setIgnoreEmptyMode(false);
- cmdHandler.register("unknown", this, "cmdUnknown");
- }
-
- /*------------------------------------------------------------------------*/
-
- throws IOException
- {
- stop();
- }
-
- /*------------------------------------------------------------------------*/
-
- public void shutdown()
- {}
-
- /*------------------------------------------------------------------------*/
-
- 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
- */
- }
-
- shutdown();
- synchronized(mainLock)
- {
- mainLock.notify();
- }
- }
- }
-
- /*==========================================================================*/
-
- private static boolean initRegistry;
- private static ScheduledExecutorService scheduler = null;
- private static RemoteInterface riface = null;
- private static ServerRecords servers;
- public UserRecords users;
- public EventRecords events;
- public volatile boolean ready = false;
-
- /*--------------------------------------------------------------------------*/
-
- Server()
- {
- servers = new ServerRecords();
- users = new UserRecords();
- events = new EventRecords();
- }
-
- /*--------------------------------------------------------------------------*/
-
- public static void usage()
- throws Utils.Shutdown
- {
- out.println("Usage: Server bindingName initRegistry serverNames\n");
- out.println("bindingName\t...the name this server shall use to bind its remote");
- out.println("\t\t reference in the RMI registry");
- out.println("initRegistry\t...a boolean value, i.e. either true or false,");
- out.println("\t\t indicating whether this server is responsible for");
- out.println("\t\t creating the RMI registry or not");
- out.println("serverNames\t...a list of names, separated by space characters,");
- out.println("\t\t indicating the name of the other servers' remote references");
-
- // 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
- {
- 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");
- }
-
- /*--------------------------------------------------------------------------*/
-
- {
- if (args.length != 3)
- usage();
-
- bindingName = args[0];
- if (bindingName.length() == 0)
- bailout("bindingName is empty");
-
- initRegistry = false;
- if (args[1].equals("true"))
- initRegistry = true;
- else if (args[1].equals("false"))
- initRegistry = false;
- else
- bailout("initRegistry must be either \"true\" or \"false\"");
-
- serverNames = args[2].split(" ");
- bailout("bindingName found in serverNames. You shouldn't do that!");
- }
-
- /*--------------------------------------------------------------------------*/
-
- public void createInterface()
- {
- int regPort = 0;
-
- try
- {
- if (in == null)
- bailout("Properties file doesn't exist or isn't readable");
- props.load(in);
- {
- if (prop.equals("registry.host"))
- regHost = props.getProperty(prop);
- else if (prop.equals("registry.port"))
- {
- try
- {
- if (regPort <= 0 || regPort > 65536)
- {
- err.println("Property " + prop + " must be a valid port number (1 - 65535). Skipping...");
- regPort = 0;
- }
- }
- {
- err.println("Property " + prop + " must be numeric. Skipping...");
- }
- }
- else
- err.println("Property " + prop + " is unknown. Skipping...");
- }
- in.close();
- }
- {
- bailout("Unable to read from properties file: " + e.getMessage());
- }
-
- if (!initRegistry && (regHost == null || regHost.length() <= 0))
- bailout("Registry host is not set");
- if (regPort == 0)
- bailout("Registry port is not set");
-
- try
- {
- if (initRegistry)
- else
-
- riface = new RemoteInterfaceImpl(this);
- //DEBUG RemoteServer.setLog(System.out);
- registry.bind(bindingName, riface);
- }
- {
- bailout("Unable to get/create registry: " + e.getMessage());
- }
- {
- registry = null;
- bailout("Unable to bind remote interface. Already bound: " + e.getMessage());
- }
-
- out.println("Remote interface successfully exported!");
- }
-
- /*--------------------------------------------------------------------------*/
-
- {
- if (file == null)
- return;
-
- try
- {
- sout.writeObject(users);
- sout.writeObject(events);
- sout.close();
- }
- {}
- {}
- }
-
- /*--------------------------------------------------------------------------*/
-
- {
- if (file == null)
- return;
-
- try
- {
- users = (UserRecords)sin.readObject();
- events = (EventRecords)sin.readObject();
- sin.close();
- }
- {}
- {}
- {}
- }
-
- /*--------------------------------------------------------------------------*/
-
- public void shutdown()
- {
- {
- UserRecord record = users.get(user);
- synchronized(record)
- {
- record.s2ciface = null;
- }
- }
-
- try
- {
- if (scheduler != null)
- {
- scheduler.shutdownNow();
- }
- }
- {}
-
- try
- {
- if (riface != null)
- riface.unexportAll();
- }
- {}
-
- try
- {
- if (registry != null)
- registry.unbind(bindingName);
- }
- {}
- {}
-
- try
- {
- if (tInteractive != null)
- {
- tInteractive.interrupt();
- tInteractive.join();
- }
- }
- {}
-
- try
- {
- if (stdin != null)
- stdin.close();
- }
- {}
-
- //PERSISTENT writeData(bindingName + ".data");
- }
-
- /*--------------------------------------------------------------------------*/
-
- {
- boolean shutdown = false;
-
- parseArgs(args);
- //PERSISTENT readData(bindingName + ".data");
-
- synchronized(mainLock)
- {
- createInterface();
-
- try
- {
- tInteractive.start();
- }
- {
- bailout("Unable to setup interactive command handler");
- }
- {
- bailout("Unable to create object output stream: " + e.getMessage());
- }
-
- try
- {
- out.println("Waiting for the other servers to come online...");
- scheduler = Executors.newScheduledThreadPool(1);
- ScheduledFuture<?> waitServerTimer = scheduler.scheduleAtFixedRate(
- new WaitForServers(), 0, 500, TimeUnit.MILLISECONDS);
- mainLock.wait();
- synchronized(servers)
- {
- if (!scheduler.isShutdown() || servers.size() != serverNames.length)
- shutdown = true;
- }
- }
- {
- /* if we get interrupted -> ignore */
- }
-
- if (!shutdown)
- {
- ready = true;
- out.println("Server startup successful!");
- try
- {
- mainLock.wait();
- }
- {
- /* if we get interrupted -> ignore */
- }
- }
- }
-
- out.println("Shutting down...");
- shutdown();
- }
-
- /*--------------------------------------------------------------------------*/
-
- {
- try
- {
- Server srv = new Server();
- srv.run(args);
- }
- catch(Utils.Shutdown e)
- {}
- }
- }
-