Subversion Repositories Code-Repo

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
87 Kevin 1
/**************************************************************************
2
 * Copyright 2011 Jules White
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 ***************************************************************************/
16
package org.vt.ece4564.latmb;
17
 
18
import java.awt.geom.Point2D;
19
import java.util.ArrayList;
20
import java.util.Calendar;
21
import java.util.Collections;
22
import java.util.GregorianCalendar;
23
import java.util.HashMap;
24
import java.util.LinkedList;
25
import java.util.List;
26
import java.util.Map;
27
import java.util.logging.Level;
28
import java.util.logging.Logger;
29
 
30
import org.jboss.netty.channel.Channel;
31
import org.jboss.netty.channel.ChannelEvent;
32
import org.jboss.netty.channel.ChannelHandlerContext;
33
import org.jboss.netty.channel.ChannelStateEvent;
34
import org.jboss.netty.channel.ExceptionEvent;
35
import org.jboss.netty.channel.MessageEvent;
36
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
37
import org.jboss.netty.channel.group.ChannelGroup;
38
import org.jboss.netty.channel.group.DefaultChannelGroup;
39
import org.vt.ece4564.latmb.LATMBProtocol.DateTime;
40
import org.vt.ece4564.latmb.LATMBProtocol.Message;
41
import org.vt.ece4564.latmb.LATMBProtocol.Position;
42
import org.vt.ece4564.latmb.LATMBProtocol.TrackingMessage;
43
import org.vt.ece4564.latmb.LATMBProtocol.Message.Builder;
44
 
45
/**
46
 * Handles a server-side channel.
47
 * 
48
 * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
49
 * @author <a href="http://gleamynode.net/">Trustin Lee</a>
50
 * 
51
 * @version $Rev: 2121 $, $Date: 2010-02-02 09:38:07 +0900 (Tue, 02 Feb 2010) $
52
 */
53
public class LATMBServerHandler extends SimpleChannelUpstreamHandler {
54
 
55
	private static final Logger logger = Logger
56
			.getLogger(LATMBServerHandler.class.getName());
57
 
58
	static final ChannelGroup channels = new DefaultChannelGroup();
59
 
60
	// Map of channels connected to chatrooms
61
	static final Map<String, ChannelGroup> chatrooms = Collections.synchronizedMap(new HashMap<String, ChannelGroup>());
62
 
63
	// Map of messages for each chatroom
64
	static final Map<String, List<LATMBMessage>> messages = Collections.synchronizedMap(new HashMap<String, List<LATMBMessage>>());
65
 
66
	// Map of channels and their locations
67
	static final Map<Channel, Point2D.Double> gpsChannels = Collections.synchronizedMap(new HashMap<Channel, Point2D.Double>());
68
 
69
	// List storing messages for GPS lookup
70
	static final List<LATMBMessage> gpsMessages = new LinkedList<LATMBMessage>();
71
 
72
 
73
	private Thread checkThread_;
74
 
75
	@Override
76
	public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e)
77
			throws Exception {
78
		if (e instanceof ChannelStateEvent) {
79
			logger.info(e.toString());
80
		}
81
		super.handleUpstream(ctx, e);
82
	}
83
 
84
	public void broadcast(TrackingMessage msg) {
85
		for (Channel c : channels) {
86
			c.write(msg);
87
		}
88
	}
89
 
90
	private synchronized void checkRecords() {
91
		if (checkThread_ == null) {
92
			checkThread_ = new Thread(new Runnable() {
93
 
94
				@Override
95
				public void run() {
96
					while (true) {
97
						try {
98
							Thread.currentThread().sleep(5000);
99
 
100
							// Removes empty chatrooms
101
							for (String str : chatrooms.keySet()) {
102
								if (chatrooms.get(str).size() == 0) {
103
									chatrooms.remove(str);
104
								}
105
							}
106
 
107
							// Removes expired messages and empty lists from chatrooms
108
							Calendar c = Calendar.getInstance();
109
							for (String str : messages.keySet()) {
110
								if (messages.get(str).size() == 0) {
111
									messages.remove(str);
112
								} else {
113
									List<LATMBMessage> toRemove = new ArrayList<LATMBMessage>();
114
									for (LATMBMessage message : messages.get(str)) {
115
										if (c.after(message.getExpiration())) {
116
											toRemove.add(message);
117
										}
118
									}
119
									for (LATMBMessage message : toRemove) {
120
										messages.get(str).remove(message);
121
									}
122
								}
123
							}
124
 
125
							// Remove expired messages from list of messages with GPS locations
126
							List<LATMBMessage> toRemove = new ArrayList<LATMBMessage>();
127
							for (LATMBMessage msg : gpsMessages) {
128
								if (c.after(msg.getExpiration())) {
129
									toRemove.add(msg);
130
								}
131
							}
132
							for (LATMBMessage message : toRemove) {
133
								gpsMessages.remove(message);
134
							}
135
 
136
						} catch (Exception e) {
137
							e.printStackTrace();
138
						}
139
					}
140
				}
141
			});
142
			checkThread_.start();
143
		}
144
	}
145
 
146
	@Override
147
	public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
148
			throws Exception {
149
 
150
		channels.add(e.getChannel());
151
 
152
		// Start the thread to check for expired entries
153
		checkRecords();
154
	}
155
 
156
	@Override
157
	public void channelDisconnected(ChannelHandlerContext ctx,
158
			ChannelStateEvent e) throws Exception {
159
		// Unregister the channel from the global channel list
160
		// so the channel does not receive messages anymore.
161
		channels.remove(e.getChannel());
162
 
163
		// Remove channel from list of GPS channels as well
164
		gpsChannels.remove(e.getChannel());
165
	}
166
 
167
	@Override
168
	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
169
 
170
		TrackingMessage tmsg = (TrackingMessage) e.getMessage();
171
 
172
		switch (tmsg.getType()) {
173
		case 0:	// Initial connection from a client
174
			System.out.println("Initial Request Message Recieved");
175
			if (tmsg.getMessage(0).hasChatroom()) {
176
				String chatroom = tmsg.getMessage(0).getChatroom();
177
				// If chatroom exists, send back all messages from that chatroom
178
				if (messages.containsKey(chatroom)) {
179
					e.getChannel().write(aggregateMessagesFromChatrooms(chatroom));
180
				}
181
				// Add the current channel to the list of channels for that chatroom
182
				if (chatrooms.containsKey(chatroom)) {
183
					chatrooms.get(chatroom).add(e.getChannel());
184
				} else {
185
					ChannelGroup channels = new DefaultChannelGroup();
186
					channels.add(e.getChannel());
187
					chatrooms.put(chatroom, channels);
188
				}
189
			} else if (tmsg.getMessage(0).hasCoordinates()) {
190
				double lat = tmsg.getMessage(0).getCoordinates().getLatitude();
191
				double lon = tmsg.getMessage(0).getCoordinates().getLongitude();
192
				double radius = tmsg.getMessage(0).getRadius();
193
 
194
				// Get messages that are within radius of channel
195
				e.getChannel().write(aggregateMessagesFromGPS(lat, lon, radius));
196
 
197
				// Save channel with its location
198
				Point2D.Double pt = new Point2D.Double(lat, lon);
199
				gpsChannels.put(e.getChannel(), pt);
200
			}
201
			break;
202
		case 1: // Saves message and broadcast it
203
			System.out.println("New Update Message Recieved");
204
			LATMBMessage msg = serializeFromProtoBuffer(tmsg);
205
			if (tmsg.getMessage(0).hasChatroom()) {
206
				String chatroom = tmsg.getMessage(0).getChatroom();
207
				// Send message to all channels connected to the chatroom
208
				if (messages.containsKey(chatroom)) {
209
					messages.get(chatroom).add(msg);
210
					for (Channel c : chatrooms.get(chatroom)) {
211
						c.write(tmsg);
212
					}
213
				} else {
214
					List<LATMBMessage> newList = new LinkedList<LATMBMessage>();
215
					newList.add(msg);
216
					messages.put(chatroom, newList);
217
					for (Channel c : chatrooms.get(chatroom)) {
218
						c.write(tmsg);
219
					}
220
				}
221
			} else if (tmsg.getMessage(0).hasCoordinates()) {
222
				// Otherwise search for channels that are within radius
223
				for (Channel ch : gpsChannels.keySet()) {
224
					double lat = gpsChannels.get(ch).getX();
225
					double lon = gpsChannels.get(ch).getY();
226
					double radius = msg.getRadius();
227
 
228
					// Send message to each channel within the radius
229
					if (getCoordinateDistance(lat, lon, msg.getLatitude(), msg.getLongitude()) <= radius) {
230
						ch.write(tmsg);
231
					}
232
				}
233
				// Save message to list of GPS associated messages
234
				gpsMessages.add(msg);
235
			}
236
			break;
237
		}
238
	}
239
 
240
	@Override
241
	public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
242
		logger.log(Level.WARNING, "Unexpected exception from downstream.",
243
				e.getCause());
244
		e.getChannel().close();
245
	}
246
 
247
	// Returns a TrackingMessage of all messages that are within radius of specified point
248
	private TrackingMessage aggregateMessagesFromGPS (double lat, double lon, double radius) {
249
		org.vt.ece4564.latmb.LATMBProtocol.TrackingMessage.Builder msg = TrackingMessage.newBuilder()
250
				.setId(0).setType(1);
251
 
252
		for (LATMBMessage entry : gpsMessages) {
253
			if (getCoordinateDistance(lat, lon, entry.getLatitude(), entry.getLongitude()) <= radius) {
254
				Calendar c = entry.getTimestamp();
255
				DateTime timestamp = DateTime.newBuilder()
256
						.setYear(c.get(Calendar.YEAR))
257
						.setMonth(c.get(Calendar.MONTH))
258
						.setDay(c.get(Calendar.DAY_OF_MONTH))
259
						.setHour(c.get(Calendar.HOUR_OF_DAY))
260
						.setMinute(c.get(Calendar.MINUTE))
261
						.setSecond(c.get(Calendar.SECOND))
262
						.build();
263
 
264
				c = entry.getExpiration();
265
				DateTime expiration = DateTime.newBuilder()
266
						.setYear(c.get(Calendar.YEAR))
267
						.setMonth(c.get(Calendar.MONTH))
268
						.setDay(c.get(Calendar.DAY_OF_MONTH))
269
						.setHour(c.get(Calendar.HOUR_OF_DAY))
270
						.setMinute(c.get(Calendar.MINUTE))
271
						.setSecond(c.get(Calendar.SECOND))
272
						.build();
273
 
274
				Builder message = Message.newBuilder()
275
						.setMessage(entry.getMessage())
276
						.setTimestamp(timestamp)
277
						.setExpiration(expiration);
278
 
279
				if (entry.getUsername() != null) {
280
					message.setUsername(entry.getUsername());
281
				}
282
 
283
				if (entry.getChatroom() != null) {
284
					message.setChatroom(entry.getChatroom());
285
				} else if (entry.getLatitude() != 0 && entry.getLongitude() != 0){
286
					Position pos = Position.newBuilder()
287
							.setLatitude(entry.getLatitude())
288
							.setLongitude(entry.getLongitude())
289
							.setAccuracy(0)
290
							.build();
291
					message.setCoordinates(pos);
292
				}
293
 
294
				if (entry.getRadius() != 0) {
295
					message.setRadius(entry.getRadius());
296
				}
297
 
298
				msg.addMessage(message);
299
			}
300
		}
301
		if (msg.isInitialized()) {
302
			return msg.build();
303
		} else {
304
			return null;
305
		}
306
	}
307
 
308
	// Returns a TrackingMessage containing all the messages from a chatroom
309
	private TrackingMessage aggregateMessagesFromChatrooms (String chatroom) {
310
		List<LATMBMessage> list = messages.get(chatroom);
311
 
312
		org.vt.ece4564.latmb.LATMBProtocol.TrackingMessage.Builder msg = TrackingMessage.newBuilder()
313
				// Type 1 == New Message
314
				.setId(0).setType(1);
315
 
316
		for (LATMBMessage entry : list) {
317
			Calendar c = entry.getTimestamp();
318
			DateTime timestamp = DateTime.newBuilder()
319
					.setYear(c.get(Calendar.YEAR))
320
					.setMonth(c.get(Calendar.MONTH))
321
					.setDay(c.get(Calendar.DAY_OF_MONTH))
322
					.setHour(c.get(Calendar.HOUR_OF_DAY))
323
					.setMinute(c.get(Calendar.MINUTE))
324
					.setSecond(c.get(Calendar.SECOND))
325
					.build();
326
 
327
			c = entry.getExpiration();
328
			DateTime expiration = DateTime.newBuilder()
329
					.setYear(c.get(Calendar.YEAR))
330
					.setMonth(c.get(Calendar.MONTH))
331
					.setDay(c.get(Calendar.DAY_OF_MONTH))
332
					.setHour(c.get(Calendar.HOUR_OF_DAY))
333
					.setMinute(c.get(Calendar.MINUTE))
334
					.setSecond(c.get(Calendar.SECOND))
335
					.build();
336
 
337
			Builder message = Message.newBuilder()
338
					.setMessage(entry.getMessage())
339
					.setTimestamp(timestamp)
340
					.setExpiration(expiration);
341
 
342
			if (entry.getUsername() != null) {
343
				message.setUsername(entry.getUsername());
344
			}
345
 
346
			if (entry.getChatroom() != null) {
347
				message.setChatroom(entry.getChatroom());
348
			} else if (entry.getLatitude() != 0 && entry.getLongitude() != 0){
349
				Position pos = Position.newBuilder()
350
						.setLatitude(entry.getLatitude())
351
						.setLongitude(entry.getLongitude())
352
						.setAccuracy(0)
353
						.build();
354
				message.setCoordinates(pos);
355
			}
356
 
357
			if (entry.getRadius() != 0) {
358
				message.setRadius(entry.getRadius());
359
			}
360
			msg.addMessage(message);
361
		}
362
		if (msg.isInitialized()) {
363
			return msg.build();
364
		} else {
365
			return null;
366
		}
367
	}
368
 
369
	// Returns a TrackingMessage created from the passed LATMBMessage
370
	private TrackingMessage serializeToProtoBuffer (LATMBMessage entry) {
371
 
372
		Calendar c = entry.getTimestamp();
373
		DateTime timestamp = DateTime.newBuilder()
374
				.setYear(c.get(Calendar.YEAR))
375
				.setMonth(c.get(Calendar.MONTH))
376
				.setDay(c.get(Calendar.DAY_OF_MONTH))
377
				.setHour(c.get(Calendar.HOUR_OF_DAY))
378
				.setMinute(c.get(Calendar.MINUTE))
379
				.setSecond(c.get(Calendar.SECOND))
380
				.build();
381
 
382
		c = entry.getExpiration();
383
		DateTime expiration = DateTime.newBuilder()
384
				.setYear(c.get(Calendar.YEAR))
385
				.setMonth(c.get(Calendar.MONTH))
386
				.setDay(c.get(Calendar.DAY_OF_MONTH))
387
				.setHour(c.get(Calendar.HOUR_OF_DAY))
388
				.setMinute(c.get(Calendar.MINUTE))
389
				.setSecond(c.get(Calendar.SECOND))
390
				.build();
391
 
392
		Builder message = Message.newBuilder()
393
				.setMessage(entry.getMessage())
394
				.setTimestamp(timestamp)
395
				.setExpiration(expiration);
396
 
397
		if (entry.getUsername() != null) {
398
			message.setUsername(entry.getUsername());
399
		}
400
 
401
		if (entry.getChatroom() != null) {
402
			message.setChatroom(entry.getChatroom());
403
		} else if (entry.getLatitude() != 0 && entry.getLongitude() != 0){
404
			Position pos = Position.newBuilder()
405
					.setLatitude(entry.getLatitude())
406
					.setLongitude(entry.getLongitude())
407
					.setAccuracy(0)
408
					.build();
409
			message.setCoordinates(pos);
410
		}
411
 
412
		if (entry.getRadius() != 0) {
413
			message.setRadius(entry.getRadius());
414
		}
415
 
416
		TrackingMessage msg = TrackingMessage.newBuilder()
417
				// Type 1 == New Message
418
				.setId(0).setType(1)
419
				.addMessage(message)
420
				.build();
421
 
422
		return msg;
423
	}
424
 
425
	// Returns a LATMBMessage from the passed TrackingMessage
426
	private LATMBMessage serializeFromProtoBuffer(TrackingMessage message) {
427
		Message msg = message.getMessage(0);
428
		LATMBMessage newEntry = new LATMBMessage();
429
 
430
		newEntry.setMessage(msg.getMessage());
431
 
432
		DateTime timestamp = msg.getTimestamp();
433
		DateTime expiration = msg.getExpiration();
434
 
435
		Calendar time = new GregorianCalendar();
436
		time.set(timestamp.getYear(), timestamp.getMonth(), timestamp.getDay(), 
437
				timestamp.getHour(), timestamp.getMinute(), timestamp.getSecond());
438
		newEntry.setTimestamp(time);
439
 
440
		Calendar exp = new GregorianCalendar();
441
		exp.set(expiration.getYear(), expiration.getMonth(), expiration.getDay(), 
442
				expiration.getHour(), expiration.getMinute(), expiration.getSecond());
443
		newEntry.setExpiration(exp);
444
 
445
		if (msg.hasUsername()) {
446
			newEntry.setUsername(msg.getUsername());
447
		} else {
448
			newEntry.setUsername("Anonymous");
449
		}
450
 
451
		if (msg.hasChatroom()) {
452
			newEntry.setChatroom(msg.getChatroom());
453
		}
454
 
455
		if (msg.hasCoordinates()) {
456
			Position coord = msg.getCoordinates();
457
			newEntry.setLatitude(coord.getLatitude());
458
			newEntry.setLongitude(coord.getLongitude());
459
		}
460
 
461
		if (msg.hasRadius()) {
462
			newEntry.setRadius(msg.getRadius());
463
		}
464
 
465
		return newEntry;
466
	}
467
 
468
	// Returns the distance (in km) between the two specified points
469
	private double getCoordinateDistance(double startLat, double startLong, double endLat, double endLong) {
470
		double d2r = Math.PI / 180;
471
 
472
	    double dlong = (endLong - startLong) * d2r;
473
	    double dlat = (endLat - startLat) * d2r;
474
	    double a =
475
	        Math.pow(Math.sin(dlat / 2.0), 2)
476
	            + Math.cos(startLat * d2r)
477
	            * Math.cos(endLat * d2r)
478
	            * Math.pow(Math.sin(dlong / 2.0), 2);
479
	    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
480
	    double d = 6367 * c;
481
 
482
	    return d;
483
	}
484
}