4 * Copyright 2015 Eddie Berrisford-Lynch <dev@fun2be.me>
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 package uk.ac.ntu.n0521366.wsyd.libs.net;
26 import java.net.InetSocketAddress;
27 import java.text.MessageFormat;
28 import java.util.ArrayList;
29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.logging.Level;
31 import java.util.logging.Logger;
34 * Maps service names on hosts to an InetAddress:Port pair.
36 * @author Eddie Berrisford-Lynch <dev@fun2be.me>
39 public class ServiceAddressMap {
41 * Multi-thread safe map keyed on the service name.
43 protected ConcurrentHashMap<String, LastSeenHost> _serviceToAddressMap = new ConcurrentHashMap<>();
46 * Encapsulates a unique network host and the last time it was seen.
48 public static class LastSeenHost {
52 * STATIC = was manually entered and should not be removed
53 * DYNAMIC = was discovered via multicast announcement or new connection and can be removed
55 public static enum STATE {STATIC, DYNAMIC}
57 public final STATE state;
58 public final long timeInMillis;
59 public final InetSocketAddress address;
62 * Constructs a new instance of a host.
64 * @param address The current address:port
65 * @param timeInMillis time last seen
66 * @param state whether record was added through dynamic discovery
68 public LastSeenHost(InetSocketAddress address, long timeInMillis, STATE state) {
69 this.address = address;
70 this.timeInMillis = timeInMillis;
75 * Construct a new instance of a dynamically announced host.
79 public LastSeenHost(InetSocketAddress host) {
80 this(host, System.currentTimeMillis(), STATE.DYNAMIC);
84 * Construct a new instance of with user-defined STATE.
86 * @param host the host address
87 * @param state DYNAMIC or STATIC
89 public LastSeenHost(InetSocketAddress host, STATE state) {
90 this(host, System.currentTimeMillis(), state);
93 * Formatted string representation of InetAddress and timestamp.
94 * @return the representation
97 public String toString() {
98 return MessageFormat.format("{0}:{1,number,integer}@{2}", this.address.getHostString(), this.address.getPort(), this.timeInMillis);
105 private static Logger LOGGER = null;
108 * Facility name for logger
110 private String _facility = null;
113 * Construct a map with a Logger.
115 * @param facility The log facility to tag messages with
116 * @param logger The Logger
118 public ServiceAddressMap(String facility, Logger logger) {
119 this._facility = facility;
128 private void log(Level level, String message) {
131 LOGGER.logp(level, _facility, null, message);
135 * Remove stale service records.
137 * @param ageInMillis milliseconds since last seen to be considered stale
138 * @return quantity of records removed
140 public ArrayList<String> cleanServiceAddressMap(long ageInMillis) {
141 ArrayList<String> result = new ArrayList<>();
142 long expireTime = System.currentTimeMillis() - ageInMillis;
143 java.util.Enumeration<String> keys = this._serviceToAddressMap.keys();
144 while (keys.hasMoreElements()) {
145 String key = keys.nextElement();
146 LastSeenHost host = _serviceToAddressMap.get(key);
147 if (host != null && host.state == LastSeenHost.STATE.DYNAMIC) {
148 if (host.timeInMillis < expireTime) {
149 if (_serviceToAddressMap.remove(key, host)) {
151 log(Level.INFO, MessageFormat.format("Removed \"{0}\" from service map", key));
160 * Ensure service is in the map of known hosts.
161 * @param service the service name to check
162 * @return true is the target service is known
164 protected boolean isServiceValid(String service) {
165 return this._serviceToAddressMap.containsKey(service);
169 * Get the current InetSocketAddress of a target.
171 * @param target name of the service
172 * @return IP address and port of the service
174 public InetSocketAddress getServiceAddress(String target) {
175 InetSocketAddress result = null;
177 if (target != null && target.length() > 0) {
178 LastSeenHost host = this._serviceToAddressMap.get(target);
180 result = host.address;
187 * Add an entry to the known services list.
188 * @param service Service name at this unique host IP address/port combination
189 * @param host new host record
190 * @return Host record previously associated with this service, or null if key didn't exist
192 public LastSeenHost put(String service, LastSeenHost host) {
193 return this._serviceToAddressMap.put(service, host);
197 * Get the host record associated with a service.
198 * @param service Service name required
199 * @return Host record associated with this service, or null if service isn't known
201 public LastSeenHost get(String service) {
202 return this._serviceToAddressMap.get(service);
210 public boolean containsService(String service) {
211 return this._serviceToAddressMap.containsKey(service);
215 * Return a Set backed by the Map so it can be iterated over.
216 * @return Set containing the Map key,value entries
218 public java.util.Set<java.util.Map.Entry<String, LastSeenHost>> getEntrySet() {
219 return _serviceToAddressMap.entrySet();