Add Registration functionality and tidy up
[WeStealzYourDataz.git] / src / uk / ac / ntu / n0521366 / wsyd / libs / net / ServiceAddressMap.java
1 /*
2  * The MIT License
3  *
4  * Copyright 2015 Eddie Berrisford-Lynch <dev@fun2be.me>
5  *
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:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
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
22  * THE SOFTWARE.
23  */
24 package uk.ac.ntu.n0521366.wsyd.libs.net;
25
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;
32
33 /**
34  * Maps service names on hosts to  an InetAddress:Port pair.
35  * 
36  * @author Eddie Berrisford-Lynch <dev@fun2be.me>
37
38  */
39 public class ServiceAddressMap {
40     /**
41      * Multi-thread safe map keyed on the service name.
42      */
43     protected ConcurrentHashMap<String, LastSeenHost> _serviceToAddressMap = new ConcurrentHashMap<>();
44
45     /**
46      * Encapsulates a unique network host and the last time it was seen.
47      */
48     public static class LastSeenHost {
49         /**
50          * State of a record:
51          * 
52          * STATIC = was manually entered and should not be removed
53          * DYNAMIC = was discovered via multicast announcement or new connection and can be removed
54          */
55         public static enum STATE {STATIC, DYNAMIC}
56         
57         public final STATE state;
58         public final long timeInMillis;
59         public final InetSocketAddress address;
60         
61         /**
62          * Constructs a new instance of a host.
63          * 
64          * @param address The current address:port
65          * @param timeInMillis time last seen
66          * @param state whether record was added through dynamic discovery
67          */
68         public LastSeenHost(InetSocketAddress address, long timeInMillis, STATE state) {
69             this.address = address;
70             this.timeInMillis = timeInMillis;
71             this.state = state;
72         }
73         
74         /**
75          * Construct a new instance of a dynamically announced host.
76          * 
77          * @param host 
78          */
79         public LastSeenHost(InetSocketAddress host) {
80             this(host, System.currentTimeMillis(), STATE.DYNAMIC);
81         }
82         
83         /**
84          * Construct a new instance of with user-defined STATE.
85          * 
86          * @param host the host address
87          * @param state DYNAMIC or STATIC
88          */
89         public LastSeenHost(InetSocketAddress host, STATE state) {
90             this(host, System.currentTimeMillis(), state);
91         }
92         /**
93          * Formatted string representation of InetAddress and timestamp.
94          * @return the representation
95          */
96         @Override
97         public String toString() {
98             return MessageFormat.format("{0}:{1,number,integer}@{2}", this.address.getHostString(), this.address.getPort(), this.timeInMillis);
99         }
100     };
101
102     /**
103      * The Logger to use.
104      */
105     private static Logger LOGGER = null;
106     
107     /**
108      * Facility name for logger
109      */
110     private String _facility = null;
111     
112     /**
113      * Construct a map with a Logger.
114      * 
115      * @param facility The log facility to tag messages with
116      * @param logger  The Logger
117      */
118     public ServiceAddressMap(String facility, Logger logger) {
119         this._facility = facility;
120         LOGGER = logger;
121     }
122
123     /**
124      * Log some message.
125      * @param level
126      * @param message 
127      */
128     private void log(Level level, String message) {
129         if (LOGGER == null)
130             return;
131         LOGGER.logp(level, _facility, null, message);
132         
133     }
134     /**
135      * Remove stale service records.
136      * 
137      * @param ageInMillis milliseconds since last seen to be considered stale
138      * @return quantity of records removed
139      */
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)) {
150                         result.add(key);
151                         log(Level.INFO, MessageFormat.format("Removed \"{0}\" from service map", key));
152                     }
153                 }
154             }
155         }
156         return result;
157     }
158
159     /**
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
163      */
164     protected boolean isServiceValid(String service) {
165         return this._serviceToAddressMap.containsKey(service);
166     }
167
168     /**
169      * Get the current InetSocketAddress of a target.
170      * 
171      * @param target name of the service
172      * @return IP address and port of the service
173      */
174     public InetSocketAddress getServiceAddress(String target) {
175         InetSocketAddress result = null;
176         
177         if (target != null && target.length() > 0) {
178             LastSeenHost host = this._serviceToAddressMap.get(target);
179             if (host != null)
180                 result = host.address;
181         }
182         
183         return result;
184     }
185     
186     /**
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
191      */
192     public LastSeenHost put(String service, LastSeenHost host) {
193         return this._serviceToAddressMap.put(service, host);
194     }
195     
196     /**
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
200      */
201     public LastSeenHost get(String service) {
202         return this._serviceToAddressMap.get(service);
203     }
204     
205     /**
206      * 
207      * @param service
208      * @return 
209      */
210     public boolean containsService(String service) {
211         return this._serviceToAddressMap.containsKey(service);
212     }
213     
214     /**
215      * Return a Set backed by the Map so it can be iterated over.
216      * @return Set containing the Map key,value entries
217      */
218     public java.util.Set<java.util.Map.Entry<String, LastSeenHost>> getEntrySet() {
219     return _serviceToAddressMap.entrySet();
220
221     }
222 }