import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
-import java.util.Date;
-import java.util.ArrayList;
-import java.text.SimpleDateFormat;
-import java.text.ParseException;
-import java.util.TreeSet;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.Map;
-import java.util.Collections;
-import java.util.logging.Logger;
-import java.util.logging.Level;
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.IOException;
-import java.io.FileNotFoundException;
+import java.io.*;
import java.net.InetSocketAddress;
-import java.net.SocketException;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.logging.LogRecord;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.logging.*;
import javax.swing.Timer;
import uk.ac.ntu.n0521366.wsyd.libs.WSYD_Member;
import uk.ac.ntu.n0521366.wsyd.libs.WSYD_Member_Comparator_UserID;
-import uk.ac.ntu.n0521366.wsyd.libs.message.MessageLogin;
-import uk.ac.ntu.n0521366.wsyd.libs.message.MessageMember;
-import uk.ac.ntu.n0521366.wsyd.libs.message.MessageMemberState;
-import uk.ac.ntu.n0521366.wsyd.libs.message.MessagePresence;
-import uk.ac.ntu.n0521366.wsyd.libs.message.MessageServerControl;
-import uk.ac.ntu.n0521366.wsyd.libs.net.ConnectionEstablishedEvent;
-import uk.ac.ntu.n0521366.wsyd.libs.net.ConnectionEstablishedEventListener;
-import uk.ac.ntu.n0521366.wsyd.libs.net.Network;
-import uk.ac.ntu.n0521366.wsyd.libs.net.NetworkMessage;
-import uk.ac.ntu.n0521366.wsyd.libs.net.NetworkMessageEvent;
-import uk.ac.ntu.n0521366.wsyd.libs.net.NetworkServerUDPMulticast;
-import uk.ac.ntu.n0521366.wsyd.libs.net.WSYD_SocketAddress;
-import uk.ac.ntu.n0521366.wsyd.libs.net.NetworkMessageEventListener;
-import uk.ac.ntu.n0521366.wsyd.libs.net.NetworkServerTCP;
-import uk.ac.ntu.n0521366.wsyd.libs.net.NetworkServerUDP;
-import uk.ac.ntu.n0521366.wsyd.libs.net.NetworkSocketClosing;
-import uk.ac.ntu.n0521366.wsyd.libs.net.NetworkStream;
-import uk.ac.ntu.n0521366.wsyd.libs.net.NetworkStreamManager;
-import uk.ac.ntu.n0521366.wsyd.libs.net.ServiceAddressMap;
-import uk.ac.ntu.n0521366.wsyd.libs.net.ServiceAddressMap.LastSeenHost;
+import uk.ac.ntu.n0521366.wsyd.libs.logging.PacketHandler;
+import uk.ac.ntu.n0521366.wsyd.libs.message.*;
+import uk.ac.ntu.n0521366.wsyd.libs.net.*;
/**
* The main Social Network server.
*
- * Can be restarted or stopped using the class static attributes
- * exitRequested and restartRequested. This can be done by an optional
- * Management GUI application.
+ * Can be restarted or stopped using the class static attributes exitRequested
+ * and restartRequested. This can be done by an optional Management GUI
+ * application.
*
* @author Eddie Berrisford-Lynch <dev@fun2be.me>
*/
public final class ServerSocial implements NetworkMessageEventListener, ConnectionEstablishedEventListener {
+
+ /**
+ * Indicates to start() loop and main() methods to exit completely.
+ */
+ public static boolean exitRequested = false;
+
+ /**
+ * Indicates to start() loop to exit, and to main() to restart the server.
+ */
+ public static boolean restartRequested = true;
+
/**
* Persistent storage in file-system when server exits.
*/
/**
* CSV test data file.
- *
+ *
* If it exists in the file system, only used if there is no _membersFile
*/
static final String _testData = "WSYD_TestData.csv";
-
+
/**
* Readable/displayable name of this application
*/
final String _title = "ServerSocial";
/**
- * Network services to address map.
+ * Name of the network log server to look for in multicast neighbour
+ * discovery
*/
- ServiceAddressMap _serviceToAddressMap;
+ private final String logServiceName = "LogService";
/**
- * Indicates to start() loop and main() methods to exit completely.
+ * Network services to address map.
*/
- public static boolean exitRequested = false;
+ ServiceAddressMap _serviceToAddressMap;
/**
- * Indicates to start() loop to exit, and to main() to restart the server.
+ * Handles display and sending of log messages.
*/
- public static boolean restartRequested = true;
+ @SuppressWarnings("NonConstantLogger")
+ private Logger LOGGER;
/**
- * Handles display and sending of log messages.
+ * Address of the Log Server.
*/
- @SuppressWarnings("NonConstantLogger")
- private static Logger LOGGER;
+ InetSocketAddress _serverLog;
/**
* SortedMap wraps a TreeMap that has been made thread-safe by
* Collections.synchronizedSortedMap() in readMembers().
*
- * Long key, the userID
- * WSYD_Member member record
+ * Long key, the userID WSYD_Member member record
*/
SortedMap<Long, WSYD_Member> _members;
ArrayList<Long> _membersOnline;
/**
- *
+ * The multicast background service thread.
*/
- WSYD_SocketAddress _multicastAdvertiserSA;
-
+ NetworkServerUDPMulticast _multicastService;
+
/**
+ * The UDP Control command listening service.
*
+ * Receives commands from the ServerManagement client.
*/
- NetworkServerUDPMulticast _multicastService;
-
- Timer _servicesAnnounce;
-
- WSYD_SocketAddress _udpControlServiceSA;
-
NetworkServerUDP _udpControlService;
-
- WSYD_SocketAddress _tcpListeningServiceSA;
-
+
+ /**
+ * The TCP user client connection listening service.
+ */
NetworkServerTCP _tcpListeningService;
-
+
+ /**
+ * Manager of user client and ServerChat TCP streams.
+ */
NetworkStreamManager _tcpStreamManager;
-
/**
+ * Whether process should announce itself using multicast.
*
+ * Provides neighbour discovery without DNS or manual IP address configuration.
+ */
+ boolean _multicastAnnouncements = false;
+
+ /**
+ * Fires ActionEvents to trigger regular tasks.
+ */
+ Timer _regularTasks;
+
+ /**
+ *
* Default constructor.
*/
public ServerSocial() {
- String[] className = this.getClass().getName().split("\\.");
- LOGGER = Logger.getLogger(className[className.length - 1]);
- LOGGER.setLevel(Level.ALL);
- if (LOGGER.getParent() != null) {
- LOGGER.getParent().setLevel(Level.ALL);
- System.out.println("Parent Logger level " + LOGGER.getParent().getLevel().toString());
+ _serverLog = null;
+ LOGGER = Logger.getLogger(_title);
+ // workaround to ensure all levels of log messages are recorded locally before the ServerLog service is available on the network
+ Logger l = LOGGER;
+ while (l != null) {
+ try {
+ l.setLevel(Level.ALL);
+ System.err.println("LOGGER: set level for logger " + l.getName());
+ } catch (SecurityException ex) {
+ System.err.println("LOGGER: cannot set level for logger " + l.getName());
+ }
+ l = l.getParent();
}
_serviceToAddressMap = new ServiceAddressMap(_title, LOGGER);
readMembers(_membersFile);
_membersOnline = new ArrayList<>();
_tcpStreamManager = new NetworkStreamManager();
}
-
+
+ public ServerSocial(boolean multicastAnnouncements) {
+ this();
+ this._multicastAnnouncements = multicastAnnouncements;
+ }
/**
- * Init listener
+ * Custom simplified logging function.
+ *
+ * Encapsulates the commonly repeated code of Logger method calls
+ *
+ * @param level the Logger.Level of this message
+ * @param format MessageFormat.format() formatter
+ * @param message zero or more arguments for MessageFormat.format() to process
*/
- ServerSocial InitListeners()
- {
- _multicastAdvertiserSA = new WSYD_SocketAddress(Network.MULTICAST_IP, Network.PORTS_MULTICAST_DISCOVERY, WSYD_SocketAddress.Protocol.UDP);
+ protected void logp(Level level, String format, Object... message) {
+ if (LOGGER != null)
+ LOGGER.logp(level, _title, null, MessageFormat.format(format, (Object[]) message));
+ }
+
+ /**
+ * Initialise the background service listener threads.
+ */
+ ServerSocial initListeners() {
+ WSYD_SocketAddress _multicastAdvertiserSA = new WSYD_SocketAddress(Network.MULTICAST_IPv4, Network.PORTS_MULTICAST_DISCOVERY, WSYD_SocketAddress.Protocol.UDP);
_multicastService = new NetworkServerUDPMulticast(_multicastAdvertiserSA, _title + "MC", _serviceToAddressMap, LOGGER);
_multicastService.getEventManager().addNetworkMessageEventListener(this, "Neighbour");
_multicastService.execute();
- _serviceToAddressMap.put("all", new LastSeenHost(new InetSocketAddress(Network.MULTICAST_IP, Network.PORTS_MULTICAST_DISCOVERY), LastSeenHost.STATE.STATIC));
-
- _udpControlServiceSA = new WSYD_SocketAddress(Network.IPv4_WILDCARD, Network.PORTS_EPHEMERAL, WSYD_SocketAddress.Protocol.UDP);
+ _serviceToAddressMap.put("all", new ServiceAddressMap.LastSeenHost(new InetSocketAddress(Network.MULTICAST_IPv4, Network.PORTS_MULTICAST_DISCOVERY), ServiceAddressMap.LastSeenHost.STATE.STATIC));
+
+ WSYD_SocketAddress _udpControlServiceSA = new WSYD_SocketAddress(Network.IPv4_WILDCARD, Network.PORTS_SERVER_CONTROL, WSYD_SocketAddress.Protocol.UDP);
_udpControlService = new NetworkServerUDP(_udpControlServiceSA, _title + "Control", _serviceToAddressMap, LOGGER);
_udpControlService.getEventManager().addNetworkMessageEventListener(this, "Control");
_udpControlService.execute();
-
- _tcpListeningServiceSA = new WSYD_SocketAddress(Network.IPv4_WILDCARD, Network.PORTS_SERVER_SOCIAL, WSYD_SocketAddress.Protocol.TCP);
+
+ WSYD_SocketAddress _tcpListeningServiceSA = new WSYD_SocketAddress(Network.IPv4_WILDCARD, Network.PORTS_SERVER_SOCIAL, WSYD_SocketAddress.Protocol.TCP);
_tcpListeningService = new NetworkServerTCP(_tcpListeningServiceSA, _title + "Listener", _serviceToAddressMap, _tcpStreamManager, LOGGER);
_tcpListeningService.addConnectionEstablisedEventListener(this);
_tcpListeningService.execute();
-
- ActionListener servicesAnnounceActionListener = new ActionListener() {
+
+ ActionListener regularTasksActionListener = new ActionListener() {
+ // provide a way to read the current version of the parent process object's fields
+ ServerSocial owner;
+
+ // XXX: Anonymous class initialisation - effectively used as the body of the object's constructor
+ {
+ // XXX: access to the outer class's instance object
+ owner = ServerSocial.this;
+ }
+
/**
* Activated by timer events to send multi-cast neighbour announcements and other regular notifications.
- * @param e
+ *
+ * @param e
*/
@Override
public void actionPerformed(ActionEvent e) {
-
- // Announce the Social Server Neighbour service
- MessagePresence mp = new MessagePresence(_title, _multicastService.getSocketAddress());
- NetworkMessage nm = NetworkMessage.createNetworkMessage("Neighbour", "all", mp);
- nm.setSender(_title + "MC");
- _multicastService.queueMessage(nm);
-
- // Notify ServerManagement of the Social Server Control service
- String target = "ServerManagementControl";
- LastSeenHost targetHost = _serviceToAddressMap.get(target);
- if (targetHost != null) {
+ // check the current value ofthe parent process' flag
+ if (owner._multicastAnnouncements) {
+ // Announce the Social Server Neighbour service
+ MessagePresence mp = new MessagePresence(_title + "MC", _multicastService.getSocketAddress());
+ NetworkMessage nm = NetworkMessage.createNetworkMessage("Neighbour", "all", mp);
+ _multicastService.queueMessage(nm);
+ // Announce the Social Server Control service
mp = new MessagePresence(_title + "Control", _udpControlService.getSocketAddress());
- nm = NetworkMessage.createNetworkMessage("Control", target, mp);
- nm.setSender(_title + "Control");
- try {
- _udpControlService.queueMessage(nm);
- LOGGER.log(Level.INFO, "Control notification sent to ServerManagement");
- } catch (IllegalArgumentException ex) {
- // Not fatal - ServerManagement may not be currently known
- }
+ nm = NetworkMessage.createNetworkMessage("Neighbour", "all", mp);
+ _multicastService.queueMessage(nm);
}
-
+
// clean up the known hosts map
ArrayList<String> servicesRemoved = _serviceToAddressMap.cleanServiceAddressMap(5000);
- for (String service: servicesRemoved) {
- // FIXME: does the process care if hosts have been removed? if not, remove this array iteration
- // TODO: If user client gone, remove from _membersOnline
- switch (service) {
+ for (String service : servicesRemoved) {
+ // If user client gone remove from online list
+ if (service != null && service.startsWith("UserID_")) {
+ String[] parts = service.split("_");
+ try {
+ long userID = Long.parseLong(parts[1]);
+ setMemberOnlineState(userID, WSYD_Member.STATE.OFFLINE);
+ logp(Level.INFO, "servicesRemoved: {0} disappeared so userID {1} made offline", service, userID);
+ } catch (NumberFormatException ex) {
+ logp(Level.WARNING, "servicesRemoved: Failed to parse userID from {0}.spit(\"_\")[1]", service);
+ }
}
}
-
}
};
-
- _servicesAnnounce = new Timer(1000, servicesAnnounceActionListener);
- _servicesAnnounce.setInitialDelay(100);
- _servicesAnnounce.start();
-
+
+ _regularTasks = new Timer(1000, regularTasksActionListener);
+ _regularTasks.setInitialDelay(100);
+ _regularTasks.start();
+
return this;
}
-
+
+ /**
+ * Update internal user online status and then notify all affected connections.
+ *
+ * @param userID the user
+ * @param state the new state
+ */
+ void setMemberOnlineState(long userID, WSYD_Member.STATE state) {
+ boolean notify = false;
+ String userName = null;
+ if (state == WSYD_Member.STATE.ONLINE)
+ if (!_membersOnline.contains(userID)) {
+ _membersOnline.add(userID);
+ userName = this._members.get(userID)._userName;
+ notify = true;
+ }
+ else
+ if (_membersOnline.contains(userID)) {
+ _membersOnline.remove(userID);
+ userName = this._members.get(userID)._userName;
+ notify = true;
+ }
+
+ if (notify) {
+ logp(Level.INFO, "User \"{0}\" (ID {1}) now {2}",userName, userID, state.toString());
+ unicastMemberPresence(userID, state);
+ }
+ }
+
+ /**
+ * Send a MemberState message to each (TCP) stream connection.
+ * @param userID the user
+ * @param state the new online state
+ */
+ private void unicastMemberPresence(long userID, WSYD_Member.STATE state) {
+ NetworkMessage message = new NetworkMessage(MessageMemberState.getType(), null, new MessageMemberState(userID, state));
+ for (long id : _membersOnline)
+ if (id != userID) {
+ NetworkStream ns = _tcpStreamManager._tcpStreams.get(id);
+ if (ns != null) {
+ ns.write(message);
+ logp(Level.FINEST, "User ID {0} state notifed to {1}", userID, ns.getRemoteAddress().toString());
+ }
+ }
+ }
+
/**
* Main execution loop of the server
*
public boolean start() throws InterruptedException {
boolean result;
- // TODO: start() create TCP listener
- // TODO: start() create UDP Multicast group listener and broadcast adverts
// wait for connections
int loopCount = 200;
- while (!ServerSocial.exitRequested && ! ServerSocial.restartRequested) {
+ while (!ServerSocial.exitRequested && !ServerSocial.restartRequested) {
Thread.sleep(1000); // wait a second
- System.out.println("start() loop " + loopCount);
- if (loopCount-- == 0)
+ logp(Level.INFO, "start() countdown {0}", loopCount);
+ if (loopCount-- == 0) {
ServerSocial.exitRequested = true;
+ }
}
- _servicesAnnounce.stop();
+ _regularTasks.stop();
_multicastService.cancel(true);
_udpControlService.cancel(true);
-
+ _tcpListeningService.cancel(true);
+
result = writeMembers(_membersFile);
return result;
boolean result = false;
try (
- FileInputStream f = new FileInputStream(fileName);
- ObjectInputStream in = new ObjectInputStream(f);
- )
- {
- if (_members == null)
- /* XXX: do not pass a Comparator to the constructor if collection is being deserialized as one was already saved during serialization.
- * If the Comparator is passed to the constructor the serialized object will 'grow' by ~17 bytes each time as multi Comparator
- * objects are written each time the collection is serialized.
- */
- _members = Collections.synchronizedSortedMap( new TreeMap<Long, WSYD_Member>() );
- if (!_members.isEmpty())
+ FileInputStream f = new FileInputStream(fileName);
+ ObjectInputStream in = new ObjectInputStream(f);) {
+ if (_members == null) /* XXX: do not pass a Comparator to the constructor if collection is being deserialized as one was already saved during serialization.
+ * If the Comparator is passed to the constructor the serialized object will 'grow' by ~17 bytes each time as multi Comparator
+ * objects are written each time the collection is serialized.
+ */ {
+ _members = Collections.synchronizedSortedMap(new TreeMap<Long, WSYD_Member>());
+ }
+ if (!_members.isEmpty()) {
_members.clear();
+ }
/* Need explicit cast to SortedMap for Object type returned by readObject()
* but this can cause an "unchecked cast" compiler warning since the compiler
* cannot be sure the Object returned from readObject() is really a
*/
@SuppressWarnings("unchecked")
SortedMap<Long, WSYD_Member> temp = (SortedMap<Long, WSYD_Member>) in.readObject();
- _members = Collections.synchronizedSortedMap( temp );
+ _members = Collections.synchronizedSortedMap(temp);
for (Map.Entry<Long, WSYD_Member> e : _members.entrySet()) {
System.out.println(e.getKey() + ": " + e.getValue().toString());
}
- LOGGER.log(Level.INFO, "Members database read from {0}", fileName);
+ logp(Level.INFO, "Members database read from {0}", fileName);
result = true;
- }
- catch(FileNotFoundException e) {
- _members = Collections.synchronizedSortedMap( new TreeMap<Long, WSYD_Member>( new WSYD_Member_Comparator_UserID() ) );
- LOGGER.log(Level.INFO, "Starting new members database: no database file found ({0})", fileName);
+ } catch (FileNotFoundException e) {
+ _members = Collections.synchronizedSortedMap(new TreeMap<Long, WSYD_Member>(new WSYD_Member_Comparator_UserID()));
+ logp(Level.INFO, "Starting new members database: no database file found ({0})", fileName);
result = true;
// if test data CSV exists import it
File csv = new File(_testData);
if (csv.exists() && csv.isFile()) {
- LOGGER.log(Level.INFO, "Importing test data from {0}", _testData);
+ logp(Level.INFO, "Importing test data from {0}", _testData);
importCSV(_testData);
}
- }
- catch(IOException e) {
- LOGGER.log(Level.SEVERE, "Unable to read database file {0}", fileName);
+ } catch (IOException e) {
+ logp(Level.SEVERE, "Unable to read database file {0}", fileName);
e.printStackTrace();
- }
- catch(ClassNotFoundException e) {
- LOGGER.log(Level.SEVERE, "Unable to deserialize database file {0}", fileName);
+ } catch (ClassNotFoundException e) {
+ logp(Level.SEVERE, "Unable to deserialize database file {0}", fileName);
e.printStackTrace();
}
{
out.writeObject(_members);
- LOGGER.log(Level.INFO, "Members database written to {0}", fileName);
+ logp(Level.INFO, "Members database written to {0}", fileName);
result = true;
- }
- catch(IOException e) {
- LOGGER.log(Level.SEVERE, "Unable to write database file {0}", fileName);
+ } catch (IOException e) {
+ logp(Level.SEVERE, "Unable to write database file {0}", fileName);
e.printStackTrace();
}
- }
- else
+ } else {
result = true;
+ }
return result;
}
/**
- * Read a CSV file containing WSYD_Member records and add it to the in-memory
- * collection.
- *
+ * Read a CSV file containing WSYD_Member records and add it to the
+ * in-memory collection.
+ *
* @param fileName name of CSV file
* @return true if successfully imported
*/
{
String line;
while ((line = br.readLine()) != null) {
- LOGGER.log(Level.FINEST, line);
+ logp(Level.FINEST, line);
try {
WSYD_Member temp = WSYD_Member.createWSYD_Member(line);
- if (temp != null)
+ if (temp != null) {
_members.put(temp._userID, temp); // add new member to collection
- } catch (IllegalArgumentException e) {
- LOGGER.log(Level.WARNING, "Ignoring bad CSV import line");
+ }
+ } catch (IllegalArgumentException e) {
+ logp(Level.WARNING, "Ignoring bad CSV import line");
}
}
- }
- catch(IOException e) {
- LOGGER.log(Level.SEVERE, "Unable to import CSV file {0}", fileName);
- e.printStackTrace();
+ } catch (IOException e) {
+ logp(Level.SEVERE, "Unable to import CSV file {0}", fileName);
+ e.printStackTrace();
}
return result;
/**
* Export WSYD_Members collection to a CSV file.
- *
+ *
* @param fileName name of the CSV file to write
* @return true if successful
*/
{
bw.write("# 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9");
bw.write("# userID, userName, password, currentLocation, bio, birthDate, interests, friends, friendsRequestsSent, friendsRequestsReceived");
- for (Map.Entry<Long, WSYD_Member> e: _members.entrySet()) {
+ for (Map.Entry<Long, WSYD_Member> e : _members.entrySet()) {
bw.write(e.getKey() + ": " + e.getValue().toString());
}
- }
- catch(IOException e) {
+ } catch (IOException e) {
LOGGER.log(Level.SEVERE, "Unable to export to CSV file {0}", fileName);
e.printStackTrace();
}
return result;
}
-
- private void notifyMemberPrescence(long userID, boolean state) {
- NetworkMessage message = new NetworkMessage("MemberNotification", null, new MessageMemberState(userID, state));
- for (long ID : _membersOnline) {
- _tcpStreamManager._tcpStreams.get(ID).write(message);
- }
- }
-
- private void memberOnline (long userID) {
- if (!_membersOnline.contains(userID)) {
- notifyMemberPrescence(userID, true);
- _membersOnline.add(userID);
- }
- }
-
- private void memberOffline (long userID) {
- if (_membersOnline.contains(userID)) {
- _membersOnline.remove(userID);
- notifyMemberPrescence(userID, false);
- }
- }
@Override
public void connectionEstablished(ConnectionEstablishedEvent event) {
- System.err.println("connectionEstablished()");
+ logp(Level.FINER, "Received ConnectionEstablishedEvent for ", event.getStream().getRemoteAddress().toString());
event.getStream().getEventManager().addNetworkMessageEventListener(this);
}
+
/**
* Process received network messages.
- *
+ *
* @param event the network message event
*/
@Override
- public void NetworkMessageReceived(NetworkMessageEvent event)
- {
+ public void NetworkMessageReceived(NetworkMessageEvent event) {
NetworkMessage nm = event.getNetworkMessage();
- if (nm == null)
+ if (nm == null) {
return;
- System.err.println("NetworkMessage received for intent " + nm.getIntent());
+ }
String type = nm.getMessage().getMessageType();
switch (nm.getIntent()) {
+ case "Neighbour":
+ if (type.equals(MessagePresence.getType())) { // Presence
+ MessagePresence mp = (MessagePresence) nm.getMessage();
+ // add or update the last-seen time of the Sender host in the known services map
+ ServiceAddressMap.LastSeenHost host = new ServiceAddressMap.LastSeenHost(new InetSocketAddress(nm.getReceivedFrom().getAddress(), mp.socketAddress.getPort()));
+ this._serviceToAddressMap.put(mp.serviceName, host);
+ logp(Level.FINEST, "Added \"{0}\" ({1}) to service map", mp.serviceName, host.address.toString());
+ switch (mp.serviceName) {
+ case "ServerChat":
+ // TODO: NetworkMessageReceived(Neighbour): prepare for keeping ServerChat in sync
+ break;
+ case logServiceName:
+ if (this._serverLog == null) {
+ logp(Level.CONFIG, "Switching to network logger");
+ this._serverLog = new InetSocketAddress(this._serviceToAddressMap.getServiceAddress(logServiceName).getAddress(), mp.socketAddress.getPort() );
+ LOGGER.addHandler(new PacketHandler(this._serverLog, null, null, _title)); // send messages to the ServerManagement client
+ LOGGER.setUseParentHandlers(false); // don't send messages locally now
+ logp(Level.CONFIG, "Switched to network logger");
+ }
+ break;
+ }
+ }
+ break;
+
case "Control": // Exit or Restart?
if (type.equals(MessageServerControl.getType())) { // ServerControl
if ("ServerManagement".equals(nm.getSender())) {
- MessageServerControl mp = (MessageServerControl)nm.getMessage();
- if (mp.exitReq == MessageServerControl.EXIT.YES) ServerSocial.exitRequested = true;
- if (mp.restartReq == MessageServerControl.RESTART.YES) ServerSocial.restartRequested = true;
+ MessageServerControl mp = (MessageServerControl) nm.getMessage();
+ logp(Level.SEVERE, "Control request from {0} for {1}", nm.getSender(), mp.toString());
+ if (mp.exitReq == MessageServerControl.EXIT.YES) {
+ ServerSocial.exitRequested = true;
+ }
+ if (mp.restartReq == MessageServerControl.RESTART.YES) {
+ ServerSocial.restartRequested = true;
+ }
}
}
break;
case "Login":
if (type.equals(MessageLogin.getType())) {
NetworkStream ns = _tcpStreamManager._tcpStreams.get(nm.getKey());
- MessageLogin ml = (MessageLogin)nm.getMessage();
-
+ MessageLogin ml = (MessageLogin) nm.getMessage();
+
Set<Map.Entry<Long, WSYD_Member>> tempSet = _members.entrySet();
Iterator<Map.Entry<Long, WSYD_Member>> tempIter = tempSet.iterator();
+ boolean userKnown = false;
while (tempIter.hasNext()) {
Map.Entry<Long, WSYD_Member> element = tempIter.next();
- if (element.getValue()._userName.equals(ml._uName) && element.getValue()._password.equals(ml._uPass)) {
- ml._userID = element.getKey();
- ml._loggedIn = true;
- _tcpStreamManager.updateKey(nm.getKey(), element.getKey()); // replace temp key in stream manager
- _membersOnline.add(element.getKey()); // make the member online
- break;
+ if (element.getValue()._userName.equals(ml._userName)) {
+ if (element.getValue()._password.equals(ml._userPassword)) {
+ ml._userID = element.getKey();
+ ml._loggedIn = true;
+ _tcpStreamManager.updateKey(nm.getKey(), element.getKey()); // replace temporary key
+ setMemberOnlineState(element.getKey(), WSYD_Member.STATE.ONLINE);
+ userKnown = true;
+ logp(Level.INFO, "Login UserID {0} ({1}) successful", ml._userID, element.getValue()._userName);
+
+ } else {
+ userKnown = true;
+ logp(Level.WARNING, "Login UserID {0} ({1}) failed", ml._userID, ml._userID, element.getValue()._userName);
+ }
+ break;
}
}
- if (ns != null)
+ if (!userKnown)
+ logp(Level.WARNING, "Login attempt by unknown user {0} from {1}", ml._userName, nm.getReceivedFrom().toString());
+ if (ns != null) {
ns.write(nm);
- else
- System.err.println("Login: cannot find stream for ID:" + nm.getKey());
+ } else {
+ logp(Level.SEVERE, "Login: cannot find stream for ID:", nm.getKey());
+ }
}
+ break;
+
case "Register":
if (type.equals(MessageMember.getType())) {
NetworkStream ns = _tcpStreamManager._tcpStreams.get(nm.getKey());
- MessageMember mm = (MessageMember)nm.getMessage();
+ MessageMember mm = (MessageMember) nm.getMessage();
// assume username can be registered unless username found
- mm.setRegistered(MessageMember.STATE.REGISTERED);
-
+ mm.setRegistered(MessageMember.STATE.UNREGISTERED);
+
Set<Map.Entry<Long, WSYD_Member>> tempSet = _members.entrySet();
Iterator<Map.Entry<Long, WSYD_Member>> tempIter = tempSet.iterator();
while (tempIter.hasNext()) {
Map.Entry<Long, WSYD_Member> element = tempIter.next();
if (element.getValue()._userName.equals(mm.getMember()._userName)) {
- mm.setRegistered(MessageMember.STATE.UNREGISTERED);
+ mm.setRegistered(MessageMember.STATE.REGISTERED);
mm.setStatus("Member already registered: " + mm.getMember()._userName);
+ logp(Level.WARNING, "Registration attempt for existing member {0}", element.getValue()._userName);
break;
}
}
// register the new member
- if (mm.getRegistered() == MessageMember.STATE.REGISTERED) {
+ if (mm.getRegistered() == MessageMember.STATE.UNREGISTERED) {
// get the next unallocated user ID
- long newUserID = _members.lastKey().longValue() + 1;
+ long newUserID = _members.lastKey() + 1;
_members.put(newUserID, mm.getMember());
// update the object so the ID can be returned to the client
mm.getMember()._userID = newUserID;
+ logp(Level.INFO, "Added new member {0} ({1})", newUserID, mm.getMember()._userName);
// update the on-disk membership data
writeMembers(_membersFile);
}
- if (ns != null)
+ if (ns != null) {
ns.write(nm);
- else
- System.err.println("Login: cannot find stream for ID:" + nm.getKey());
-
+ } else {
+ logp(Level.WARNING, "Login: cannot find stream for ID: ", nm.getKey());
+ }
+
}
break;
- default:
- System.err.println("Unhandled NetworkMessage received for intent " + nm.getIntent());
+ default: // log all unhandled messages
+ logp(Level.WARNING, "Unhandled NetworkMessage received with intent: \"{0}\" sender: \"{1}\" target: \"{2}\"", nm.getIntent(), nm.getSender(), nm.getTarget());
}
-
+
}
-
/**
* Entry point which starts, restarts, and exits the application.
- *
+ *
* @param args the command line arguments
- * @throws java.lang.InterruptedException
*/
- public static void main(String[] args) throws InterruptedException {
- while (!ServerSocial.exitRequested && ServerSocial.restartRequested) {
- ServerSocial app = new ServerSocial().InitListeners();
- ServerSocial.restartRequested = false;
- if (!app.start()) {
- System.err.println("Encountered error running Social Server");
- break; // leave the while loop
+ public static void main(String[] args) {
+ class App implements Runnable {
+ public boolean multicastAnnouncements = true;
+
+ @Override
+ public void run() {
+ while (!ServerSocial.exitRequested && ServerSocial.restartRequested) {
+ ServerSocial app = new ServerSocial(multicastAnnouncements).initListeners();
+ ServerSocial.restartRequested = false;
+ try {
+ if (!app.start()) {
+ Logger.getLogger(ServerSocial.class.getName()).log(Level.SEVERE, null, "Encountered error running Social Server");
+ break; // leave the while loop
+ }
+ } catch (InterruptedException ex) {
+ Logger.getLogger(ServerSocial.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ }
+ }
+ App app = new App();
+
+ // process command line arguments
+ for (String arg : args) {
+ switch (arg) {
+ case "-announce":
+ app.multicastAnnouncements = true;
+ break;
+ case "-noannounce":
+ app.multicastAnnouncements = false;
+ break;
}
}
+ app.run();
}
}