Move ServiceToHostMap into stand-alone class ServiceAddressMap and refactor accordingly
authorTJ <hacker@iam.tj>
Sun, 3 May 2015 18:50:30 +0000 (19:50 +0100)
committerTJ <hacker@iam.tj>
Sun, 3 May 2015 18:50:30 +0000 (19:50 +0100)
src/uk/ac/ntu/n0521366/wsyd/libs/net/NetworkServerAbstract.java
src/uk/ac/ntu/n0521366/wsyd/libs/net/NetworkServerUDP.java
src/uk/ac/ntu/n0521366/wsyd/libs/net/NetworkServerUDPMulticast.java
src/uk/ac/ntu/n0521366/wsyd/libs/net/ServiceAddressMap.java [new file with mode: 0644]
src/uk/ac/ntu/n0521366/wsyd/management/ServerManagement.java

index 691afc6..4119a5c 100644 (file)
@@ -101,6 +101,7 @@ public abstract class NetworkServerAbstract extends SwingWorker<Integer, Network
      */
     WSYD_SocketAddress _socketAddress;
 
+    protected ServiceAddressMap _serviceToHostMap;
     /**
      * Thread safe First In, First Out Queue of NetworkMessage objects waiting to be sent.
      * 
@@ -109,44 +110,6 @@ public abstract class NetworkServerAbstract extends SwingWorker<Integer, Network
      */
     protected ConcurrentLinkedQueue<NetworkMessage> _sendMessageQueue = new ConcurrentLinkedQueue<>();
 
-    /**
-     * Encapsulates a unique network host and the last time it was seen.
-     */
-    protected class LastSeenHost {
-        final long timeInMillis;
-        final InetSocketAddress address;
-        
-        LastSeenHost(InetSocketAddress address, long timeInMillis) {
-            this.address = address;
-            this.timeInMillis = timeInMillis;
-        }
-        LastSeenHost(InetSocketAddress host) {
-            this(host, System.currentTimeMillis());
-        }
-        
-        /**
-         * Formatted string representation of IneAddress and timestamp.
-         * @return the representation
-         */
-        @Override
-        public String toString() {
-            return MessageFormat.format("{0}:{1,number,integer}@{2}", this.address.getHostString(), this.address.getPort(), this.timeInMillis);
-        }
-    };
-    /**
-     * Maps service _title to its parent network host.
-     * <p>
-     * Used by methods on the Owner Thread to determine the list of valid service
-     * names it can submit messages to (by iterating the keys using keySet()).</p>
-     * <p>
-     * New service names can be added in two ways:<br/>
-     * <ol>
-     *  <li>by the Worker Thread from received messages</li>
-     *  <li>by the Owner or (other thread) from a service discovery helper (such as multicast discovery)</li>
-     * </ol>
-     */
-    protected ConcurrentHashMap<String, LastSeenHost> _serviceToHostMap = new ConcurrentHashMap<>();;
-
     /**
      * Wrapper for filtering NetworkMessageEvents based on the message intent
      */
@@ -209,6 +172,7 @@ public abstract class NetworkServerAbstract extends SwingWorker<Integer, Network
         this._connectionCount = 0;
         this._title = null;
         this._socketAddress = null;
+        this._serviceToHostMap = null;
     }
     
     /**
@@ -218,12 +182,14 @@ public abstract class NetworkServerAbstract extends SwingWorker<Integer, Network
      * 
      * @param socketAddress The socket to listen on
      * @param title source identifier for use in log messages and sent NetworkMessage objects
+     * @param serviceToHostMap the map object used for host <> InetSocketAddress lookups
      * @param logger An instance of Logger to be used by all objects of this class
      */
-    public NetworkServerAbstract(WSYD_SocketAddress socketAddress, String title, Logger logger) {
+    public NetworkServerAbstract(WSYD_SocketAddress socketAddress, String title, ServiceAddressMap serviceToHostMap, Logger logger) {
         this._connectionCount = 0;
         this._title = title;
         this._socketAddress = socketAddress;
+        this._serviceToHostMap = serviceToHostMap;
         if (LOGGER == null) // do not replace existing logger reference
             LOGGER = logger;
     }
@@ -235,9 +201,10 @@ public abstract class NetworkServerAbstract extends SwingWorker<Integer, Network
      * 
      * @param socketAddress The socket to listen on
      * @param title source identifier for use in log messages and sent NetworkMessage objects
+     * @param serviceToHostMap the map object used for host <> InetSocketAddress lookups
      */
-    public NetworkServerAbstract(WSYD_SocketAddress socketAddress, String title) {
-        this(socketAddress, title, null);
+    public NetworkServerAbstract(WSYD_SocketAddress socketAddress, String title, ServiceAddressMap serviceToHostMap) {
+        this(socketAddress, title, serviceToHostMap, null);
     }
 
     /**
@@ -403,16 +370,6 @@ public abstract class NetworkServerAbstract extends SwingWorker<Integer, Network
     @Override
     protected abstract void done();
 
-
-    /**
-     * Ensure service is in the map of known hosts.
-     * @param service the service name to check
-     * @return true is the target service is known
-     */
-    protected boolean isServiceValid(String service) {
-        return this._serviceToHostMap.containsKey(service);
-    }
-
     /**
      * Adds a message to the queue of pending messages.
      * 
@@ -429,7 +386,7 @@ public abstract class NetworkServerAbstract extends SwingWorker<Integer, Network
             String target = message.getTarget();
             if (target == null)
                 throw new IllegalArgumentException("target cannot be null");
-            if(!isServiceValid(target))
+            if(!_serviceToHostMap.isServiceValid(target))
                 throw new IllegalArgumentException("target service does not exist: " + target);
             
             NetworkMessage temp;
@@ -444,24 +401,6 @@ public abstract class NetworkServerAbstract extends SwingWorker<Integer, Network
         return result;
     }
 
-    /**
-     * Get the current InetAddress of a target from the services map.
-     * 
-     * @param target name of the service
-     * @return IP address and port of the service
-     */
-    public InetSocketAddress getTargetAddress(String target) {
-        InetSocketAddress result = null;
-        
-        if (target != null && target.length() > 0) {
-            LastSeenHost host = this._serviceToHostMap.get(target);
-            if (host != null)
-                result = host.address;
-        }
-        
-        return result;
-    }
-
     /**
      * Add a NetworkMessageEvent listener.
      * 
index f3ad6e5..b514fa8 100644 (file)
@@ -35,6 +35,7 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.logging.LogRecord;
 import uk.ac.ntu.n0521366.wsyd.libs.message.MessageLogRecord;
+import uk.ac.ntu.n0521366.wsyd.libs.net.ServiceAddressMap;
 
 
 
@@ -63,10 +64,11 @@ public class NetworkServerUDP extends NetworkServerAbstract {
      * 
      * @param socketAddress The socket to listen on
      * @param title source identifier for use in log messages and sent NetworkMessage objects
+     * @param serviceToHostMap the map object used for host <> InetSocketAddress lookups
      * @param logger An instance of Logger to be used by all objects of this class
      */
-    public NetworkServerUDP(WSYD_SocketAddress socketAddress, String title, Logger logger) {
-        super(socketAddress, title, logger);
+    public NetworkServerUDP(WSYD_SocketAddress socketAddress, String title, ServiceAddressMap serviceToHostMap, Logger logger) {
+        super(socketAddress, title, serviceToHostMap, logger);
     }
 
     /**
@@ -76,9 +78,10 @@ public class NetworkServerUDP extends NetworkServerAbstract {
      * 
      * @param socketAddress The socket to listen on
      * @param title source identifier for use in log messages and sent NetworkMessage objects
+     * @param serviceToHostMap the map object used for host <> InetSocketAddress lookups
      */
-    public NetworkServerUDP(WSYD_SocketAddress socketAddress, String title) {
-        super(socketAddress, title);
+    public NetworkServerUDP(WSYD_SocketAddress socketAddress, String title, ServiceAddressMap serviceToHostMap) {
+        super(socketAddress, title, serviceToHostMap);
     }
 
     /**
@@ -145,7 +148,7 @@ public class NetworkServerUDP extends NetworkServerAbstract {
             if (!messageReceived.getSender().equals(_title)) {
             
                 // add or update the last-seen time of the Sender host in the known services map
-                LastSeenHost host = new LastSeenHost((InetSocketAddress)packetReceive.getSocketAddress());
+                ServiceAddressMap.LastSeenHost host = new ServiceAddressMap.LastSeenHost((InetSocketAddress)packetReceive.getSocketAddress());
                 this._serviceToHostMap.put(messageReceived.getSender(), host);
                 log(Level.FINEST, _title, MessageFormat.format("Added \"{0}\" to service map", messageReceived.getSender()));
 
@@ -199,7 +202,7 @@ public class NetworkServerUDP extends NetworkServerAbstract {
         boolean result = false;
 
         if (message != null) {
-            LastSeenHost host = _serviceToHostMap.get(message.getTarget());
+            ServiceAddressMap.LastSeenHost host = _serviceToHostMap.get(message.getTarget());
             if (host != null) {
                 InetSocketAddress address = host.address;
                 if (address != null) {
index 28c3f4f..559e81b 100644 (file)
@@ -31,6 +31,7 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.ArrayList;
 import java.util.Enumeration;
+import uk.ac.ntu.n0521366.wsyd.libs.net.ServiceAddressMap;
 
 /**
  *
@@ -52,12 +53,13 @@ public class NetworkServerUDPMulticast extends NetworkServerUDP {
      * 
      * @param socketAddress The socket to listen on
      * @param title source identifier for use in log messages and sent NetworkMessage objects
+     * @param serviceToHostMap the map object used for host <> InetSocketAddress lookups
      * @param logger An instance of Logger to be used by all objects of this class
      */
-    public NetworkServerUDPMulticast(WSYD_SocketAddress socketAddress, String title, Logger logger) {
-        super(socketAddress, title, logger);
+    public NetworkServerUDPMulticast(WSYD_SocketAddress socketAddress, String title, ServiceAddressMap serviceToHostMap, Logger logger) {
+        super(socketAddress, title, serviceToHostMap, logger);
         // permit broadcasting to pseudo-host 'all' since this is multicast
-        this._serviceToHostMap.put("all", new LastSeenHost(socketAddress.getSocketAddress()));
+        this._serviceToHostMap.put("all", new ServiceAddressMap.LastSeenHost(socketAddress.getSocketAddress()));
     }
 
     /**
@@ -67,9 +69,10 @@ public class NetworkServerUDPMulticast extends NetworkServerUDP {
      * 
      * @param socketAddress The socket to listen on
      * @param title source identifier for use in log messages and sent NetworkMessage objects
+     * @param serviceToHostMap the map object used for host <> InetSocketAddress lookups
      */
-    public NetworkServerUDPMulticast(WSYD_SocketAddress socketAddress, String title) {
-        super(socketAddress, title);
+    public NetworkServerUDPMulticast(WSYD_SocketAddress socketAddress, String title, ServiceAddressMap serviceToHostMap) {
+        super(socketAddress, title, serviceToHostMap);
     }
 
     /**
@@ -143,36 +146,4 @@ public class NetworkServerUDPMulticast extends NetworkServerUDP {
             
         }
     }
-
-    /**
-     * Remove stale service records.
-     * 
-     * @param ageInMillis milliseconds since last seen to be considered stale
-     * @return quantity of records removed
-     */
-    public ArrayList<String> cleanServiceToHostMap(long ageInMillis) {
-        ArrayList<String> result =  new ArrayList<>();
-        long expireTime = System.currentTimeMillis() - ageInMillis;
-        java.util.Enumeration<String> keys = this._serviceToHostMap.keys();
-        while (keys.hasMoreElements()) {
-            String key = keys.nextElement();
-
-            // XXX: special handling for "all" target - never remove it
-            if (!key.equals("all")) {
-                LastSeenHost host = _serviceToHostMap.get(key);
-                if (host != null) {
-                    if (host.timeInMillis < expireTime) {
-                        if (_serviceToHostMap.remove(key, host)) {
-                            result.add(key);
-                            ArrayList<String> messages = new ArrayList<>();
-                            messages.add(key);
-                            log(Level.INFO, _title, "Removed \"{0}\" from service map", messages);
-                        }
-                    }
-                }
-            }
-        }
-        return result;
-    }
-
 }
diff --git a/src/uk/ac/ntu/n0521366/wsyd/libs/net/ServiceAddressMap.java b/src/uk/ac/ntu/n0521366/wsyd/libs/net/ServiceAddressMap.java
new file mode 100644 (file)
index 0000000..72f4fef
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2015 TJ <hacker@iamtj>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package uk.ac.ntu.n0521366.wsyd.libs.net;
+
+import java.net.InetSocketAddress;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Maps service names on hosts to  an InetAddress:Port pair.
+ * 
+ * @author TJ <hacker@iamtj>
+ */
+public class ServiceAddressMap {
+    /**
+     * Multi-thread safe map keyed on the service name.
+     */
+    protected ConcurrentHashMap<String, LastSeenHost> _serviceToAddressMap = new ConcurrentHashMap<>();
+
+    /**
+     * Encapsulates a unique network host and the last time it was seen.
+     */
+    public static class LastSeenHost {
+        /**
+         * State of a record:
+         * 
+         * STATIC = was manually entered and should not be removed
+         * DYNAMIC = was discovered via multicast announcement or new connection and can be removed
+         */
+        public static enum STATE {STATIC, DYNAMIC}
+        
+        final STATE state;
+        final long timeInMillis;
+        final InetSocketAddress address;
+        
+        /**
+         * Constructs a new instance of a host.
+         * 
+         * @param address The current address:port
+         * @param timeInMillis time last seen
+         * @param state whether record was added through dynamic discovery
+         */
+        LastSeenHost(InetSocketAddress address, long timeInMillis, STATE state) {
+            this.address = address;
+            this.timeInMillis = timeInMillis;
+            this.state = state;
+        }
+        
+        /**
+         * Construct a new instance of a dynamically announced host.
+         * 
+         * @param host 
+         */
+        LastSeenHost(InetSocketAddress host) {
+            this(host, System.currentTimeMillis(), STATE.DYNAMIC);
+        }
+        
+        /**
+         * Formatted string representation of InetAddress and timestamp.
+         * @return the representation
+         */
+        @Override
+        public String toString() {
+            return MessageFormat.format("{0}:{1,number,integer}@{2}", this.address.getHostString(), this.address.getPort(), this.timeInMillis);
+        }
+    };
+
+    /**
+     * The Logger to use.
+     */
+    private static Logger LOGGER = null;
+    
+    /**
+     * Facility name for logger
+     */
+    private String _facility = null;
+    
+    /**
+     * Construct a map with a Logger.
+     * 
+     * @param facility The log facility to tag messages with
+     * @param logger  The Logger
+     */
+    public ServiceAddressMap(String facility, Logger logger) {
+        this._facility = facility;
+        LOGGER = logger;
+    }
+
+    /**
+     * Log some message.
+     * @param level
+     * @param message 
+     */
+    private void log(Level level, String message) {
+        if (LOGGER == null)
+            return;
+        LOGGER.logp(level, _facility, null, message);
+        
+    }
+    /**
+     * Remove stale service records.
+     * 
+     * @param ageInMillis milliseconds since last seen to be considered stale
+     * @return quantity of records removed
+     */
+    public ArrayList<String> cleanServiceAddressMap(long ageInMillis) {
+        ArrayList<String> result = new ArrayList<>();
+        long expireTime = System.currentTimeMillis() - ageInMillis;
+        java.util.Enumeration<String> keys = this._serviceToAddressMap.keys();
+        while (keys.hasMoreElements()) {
+            String key = keys.nextElement();
+
+            // XXX: special handling for "all" target - never remove it
+            LastSeenHost host = _serviceToAddressMap.get(key);
+            if (host != null && host.state == LastSeenHost.STATE.DYNAMIC) {
+                if (host.timeInMillis < expireTime) {
+                    if (_serviceToAddressMap.remove(key, host)) {
+                        result.add(key);
+                        log(Level.INFO, MessageFormat.format("Removed \"{0}\" from service map", key));
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Ensure service is in the map of known hosts.
+     * @param service the service name to check
+     * @return true is the target service is known
+     */
+    protected boolean isServiceValid(String service) {
+        return this._serviceToAddressMap.containsKey(service);
+    }
+
+    /**
+     * Get the current InetSocketAddress of a target.
+     * 
+     * @param target name of the service
+     * @return IP address and port of the service
+     */
+    public InetSocketAddress getServiceAddress(String target) {
+        InetSocketAddress result = null;
+        
+        if (target != null && target.length() > 0) {
+            LastSeenHost host = this._serviceToAddressMap.get(target);
+            if (host != null)
+                result = host.address;
+        }
+        
+        return result;
+    }
+    
+    public void put(String service, LastSeenHost host) {
+        this._serviceToAddressMap.put(service, host);
+    }
+    
+    public LastSeenHost get(String service) {
+        return this._serviceToAddressMap.get(service);
+    }
+}
index 80cdee1..575d2ed 100644 (file)
@@ -29,6 +29,7 @@ import java.io.File;
 import javax.swing.ImageIcon;
 import java.awt.Desktop;
 import java.net.URI;
+import java.net.InetAddress;
 import java.io.IOException;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -61,7 +62,7 @@ public class ServerManagement extends javax.swing.JFrame implements NetworkMessa
     /**
      * class-wide logger
      */
-    static final Logger LOGGER = Logger.getLogger("ServerManagement");
+    Logger LOGGER = null;
     
     /**
      * Location of resource bundles, images, icons
@@ -71,8 +72,9 @@ public class ServerManagement extends javax.swing.JFrame implements NetworkMessa
     /**
      * Readable/displayable name of this application
      */
-    String _title;
+    final String _title = "ServerManagement";
 
+    ServiceAddressMap _serviceToAddressMap;
     /**
      * The UDP listener address for incoming log messages
      */
@@ -92,7 +94,8 @@ public class ServerManagement extends javax.swing.JFrame implements NetworkMessa
      * Multi-cast neighbour advertise and discover service in a SwingWorker thread.
      */
     NetworkServerUDPMulticast _multicastServer = null;
-    
+    boolean _multicastAnnouncements = false;
 
     /**
      * Enable or suspend logging
@@ -116,8 +119,10 @@ public class ServerManagement extends javax.swing.JFrame implements NetworkMessa
      * @see #initListeners()
      */
     public ServerManagement() {
-        _title = "ServerManagement";
+        if (LOGGER == null) // single instance of the Logger shared by all class objects
+            LOGGER = Logger.getLogger(_title);
         LOGGER.setLevel(Level.ALL);
+        _serviceToAddressMap = new ServiceAddressMap(_title, LOGGER);
         initComponents();
         _doLogging = true;
         _autoScroll = true;
@@ -126,6 +131,16 @@ public class ServerManagement extends javax.swing.JFrame implements NetworkMessa
         gDialogAbout.getContentPane().setBackground(gDialogAbout.getBackground());
     }
 
+    /**
+     * Creates new Server Management GUI that also announces itself using Multicast Neighbour discovery.
+     * @param multicastAnnouncements true if it should announce itself
+     * @param serverSocial IP address of ServerSocial - if non-null prevents ServerSocial map entry being treated as stale
+     */
+    public ServerManagement(boolean multicastAnnouncements, InetAddress serverSocial) {
+        this();
+        this._multicastAnnouncements = multicastAnnouncements;
+        // TODO: implement constructor setting IP address of SocialServer from command-line value - needs ServiceToHostMap separating from NetworkServerAbstract and do not allow manually added service map element being treated as stale
+    }
     /**
      * Initialise listeners and other objects that require a reference to 'this'.
      * 
@@ -144,52 +159,57 @@ public class ServerManagement extends javax.swing.JFrame implements NetworkMessa
         LOGGER.log(Level.INFO, "Server Management starting");
         
         _logServerSA = new WSYD_SocketAddress(Network.PORTS_SERVER_LOG, Protocol.UDP);
-        _logServer = new NetworkServerUDP(_logServerSA, "ServerLog", LOGGER);
+        _logServer = new NetworkServerUDP(_logServerSA, _title, _serviceToAddressMap, LOGGER);
         _logServer.addNetworkMessageEventListener(this, "Log");
         _logServer.setSimulate(false);
         _logServer.execute();
+
         _multicastAdvertiserSA = new WSYD_SocketAddress(Network.MULTICAST_IP, Network.PORTS_MULTICAST_DISCOVERY, Protocol.UDP);
-        _multicastServer = new NetworkServerUDPMulticast(_multicastAdvertiserSA, "ServerLogMC", LOGGER);
+        _multicastServer = new NetworkServerUDPMulticast(_multicastAdvertiserSA, "ServerManagementMC", _serviceToAddressMap, LOGGER);
         _multicastServer.addNetworkMessageEventListener(this, "Neighbour");
         _multicastServer.execute();
-
-        ActionListener multicastAnnounceActionListener = new ActionListener() {
-            /**
-             * Activated by timer events to send multi-cast neighbour announcements for the Log Service.
-             * @param e 
-             */
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                // Create local log report first
-                LogRecord record = new LogRecord(Level.FINEST, "Multicast: Announcing Presence");
-                record.setSourceClassName("ServerLog");
-                record.setMillis(System.currentTimeMillis());
-                LOGGER.log(record);
-
-                // Announce the Log Server service
-                MessagePresence mp = new MessagePresence("ServerLog", Network.PORTS_SERVER_LOG);
-                NetworkMessage nm = NetworkMessage.createNetworkMessage("Neighbour", "all", mp);
-                nm.setSender("ServerLog");
-                _multicastServer.queueMessage(nm);
-                
-                // clean up the known hosts map and keep Server menu up-to-date
-                ArrayList<String> servicesRemoved = _multicastServer.cleanServiceToHostMap(5000);
-                for (String service: servicesRemoved) {
+        
+        if (this._multicastAnnouncements) {
+            ActionListener multicastAnnounceActionListener = new ActionListener() {
+                /**
+                 * Activated by timer events to send multi-cast neighbour
+                 * announcements for the Log Service.
+                 *
+                 * @param e
+                 */
+                @Override
+                public void actionPerformed(ActionEvent e) {
+                    // Create local log report first
+                    LogRecord record = new LogRecord(Level.FINEST, "Multicast: Announcing Presence");
+                    record.setSourceClassName("ServerLog");
+                    record.setMillis(System.currentTimeMillis());
+                    LOGGER.log(record);
+
+                    // Announce the Log Server service
+                    MessagePresence mp = new MessagePresence("ServerLog", Network.PORTS_SERVER_LOG);
+                    NetworkMessage nm = NetworkMessage.createNetworkMessage("Neighbour", "all", mp);
+                    nm.setSender("ServerLog");
+                    _multicastServer.queueMessage(nm);
+
+                    // clean up the known hosts map and keep Server menu up-to-date
+                    ArrayList<String> servicesRemoved =  _serviceToAddressMap.cleanServiceAddressMap(5000);
+                    for (String service : servicesRemoved) {
                         switch (service) {
                             case "ServerSocial":
                                 gMenuServerSocial.setEnabled(false);
                                 break;
                             case "ServerChat":
                                 gMenuServerChat.setEnabled(false);
-                                break;                                
+                                break;
                         }
-                }
+                    }
 
-            }
-        };
-        multicastAnnounce = new Timer(1000, multicastAnnounceActionListener);
-        multicastAnnounce.setInitialDelay(100);
-        multicastAnnounce.start();
+                }
+            };
+            multicastAnnounce = new Timer(1000, multicastAnnounceActionListener);
+            multicastAnnounce.setInitialDelay(100);
+            multicastAnnounce.start();
+        }
         
         return this;
     }
@@ -231,7 +251,7 @@ public class ServerManagement extends javax.swing.JFrame implements NetworkMessa
         boolean result = false;
 
         if (message != null) {
-            InetSocketAddress address = this._multicastServer.getTargetAddress(message.getTarget());
+            InetSocketAddress address = this._serviceToAddressMap.getServiceAddress(message.getTarget());
             if (address != null) {
                 message.setSender("ServerManagement");
                 try {
@@ -244,7 +264,7 @@ public class ServerManagement extends javax.swing.JFrame implements NetworkMessa
                     DatagramSocket socket = new DatagramSocket();
                     // acknowledge receipt
                     socket.send(packetSend);
-                    System.err.println(MessageFormat.format("Sending packet for {0} to {1} ({3}:{4}) from {2}", message.getIntent(), message.getTarget(), message.getSender(), packetSend.getAddress().getHostAddress(), packetSend.getPort()));
+                    LOGGER.logp(Level.FINEST, _title, null, MessageFormat.format("Sending packet for {0} to {1} ({3}:{4,number,integer}) from {2}", message.getIntent(), message.getTarget(), message.getSender(), packetSend.getAddress().getHostAddress(), packetSend.getPort()));
 
                     result = true; // successful
                 } catch (IOException e) {
@@ -469,10 +489,10 @@ public class ServerManagement extends javax.swing.JFrame implements NetworkMessa
 
     private void gMenuServerSocialRestartActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_gMenuServerSocialRestartActionPerformed
         // TODO add your handling code here:
-        System.err.println("Requesting ServerSocial Restart");
+        LOGGER.logp(Level.FINEST, _title, null, "Requesting ServerSocial Restart");
         MessageServerControl mc = new MessageServerControl(MessageServerControl.EXIT.NO , MessageServerControl.RESTART.YES);
-        NetworkMessage nm = NetworkMessage.createNetworkMessage("Control", "ServerSocial", mc);
-        nm.setSender("ServerLog");
+        NetworkMessage nm = NetworkMessage.createNetworkMessage("Control", "ServerSocialControl", mc);
+        nm.setSender(_title);
         UDPSend(nm);
 
                 
@@ -587,7 +607,6 @@ public class ServerManagement extends javax.swing.JFrame implements NetworkMessa
                 this.isLoggable(m.record);
                 break;
             case "Neighbour":
-                // TODO: NetworkMessageReceived(): Test Multicast message received handler to enable menu items for recognised servers
                 String type = nm.getMessage().getMessageType();
                     if (type.equals(MessagePresence.getType())) { // Presence
                         MessagePresence mp = (MessagePresence)nm.getMessage();
@@ -635,19 +654,43 @@ public class ServerManagement extends javax.swing.JFrame implements NetworkMessa
         //</editor-fold>
         //</editor-fold>
         //</editor-fold>
-
         /* Create and display the form */
-        java.awt.EventQueue.invokeLater(new Runnable() {
+        class App implements Runnable {
+            public boolean multicastAnnouncements = false;
+            public InetAddress serverSocial = null;
             @Override
             public void run() {
                 try {
-                new ServerManagement().initListeners().setVisible(true);
+                new ServerManagement(multicastAnnouncements, serverSocial).initListeners().setVisible(true);
                 }
                 catch(UnknownHostException e) {
                     System.err.println("Error: cannot create log server listener socket");
                 }
             }
-        });
+        }
+        App app = new App();
+        
+        // process command line arguments        
+        for (int i = 0; i < args.length; i++) {
+            switch (args[i]) {
+                case "-announce":
+                    app.multicastAnnouncements = true;
+                    break;
+                case "-server":
+                    if (args.length >= i+1) {
+                        // read the next argument as an IP address
+                        InetAddress temp;
+                        try {
+                            temp = InetAddress.getByName(args[i+1]);
+                            app.serverSocial = temp;
+                        } catch (UnknownHostException e) {
+                            System.err.println(MessageFormat.format("Error: {0} is not a valid hostname or IP address", args[i+1]));
+                        }
+                    }
+                break;
+            }
+        }
+        java.awt.EventQueue.invokeLater(app);
     }
 
     // Variables declaration - do not modify//GEN-BEGIN:variables