View Javadoc

1   /*
2    * Trip Tracker, a real-time position tracking system for the Internet.
3    * Copyright (C) 2006  Team Trip Tracker
4    *
5    * This program is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License as published by the
7    * Free Software Foundation; either version 2 of the License, or (at your
8    * option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful, but
11   * WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13   * General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License along
16   * with this program; if not, write to the Free Software Foundation, Inc.,
17   * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18   */
19  
20  package triptracker.server;
21  
22  import static triptracker.core.Protocol.*;
23  
24  import java.io.BufferedReader;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.InputStreamReader;
28  import java.io.OutputStream;
29  import java.io.PrintStream;
30  import java.net.Socket;
31  import java.util.List;
32  
33  import triptracker.core.Route;
34  import triptracker.core.User;
35  import triptracker.server.persistence.MySqlDB;
36  import triptracker.server.persistence.PersistenceAdapter;
37  
38  /***
39   * Message handler for connected clients.
40   */
41  public abstract class ClientHandler {
42  	protected PersistenceAdapter db = new MySqlDB(); 
43  	protected Server server;
44  	protected User user;
45  	protected Route route;
46  	
47  	private final Socket socket;
48  	protected final InputStream inStream;
49  	protected final OutputStream outStream;
50  	protected final BufferedReader in;
51  	protected final PrintStream out;
52  
53  	private DataReaderThread readerThread;
54  	
55  	/***
56  	 * Sets up socket streams for client communication and creates the message
57  	 * reader thread for message processing. 
58  	 * 
59  	 * @param server main server instance
60  	 * @param socket client communication socket
61  	 * @param user client user data
62  	 * @throws IOException on connection failure
63  	 */
64  	protected ClientHandler(Server server, Socket socket, User user) throws IOException {
65  		this.server = server;
66  		this.socket = socket;
67  		this.user = user;
68  		db.setUserId(user.getUserId());
69  		
70  		// Setup streams.
71  		inStream = socket.getInputStream();
72  		outStream = socket.getOutputStream();
73  		in = new BufferedReader(new InputStreamReader(inStream));
74  		out = new PrintStream(outStream, true);
75  		
76  		readerThread = new DataReaderThread();
77  	}
78  	
79  	/***
80  	 * Invoked when the client has disconnected. Sub-classes can override this
81  	 * method to implement their own clean up procedure when a connection is
82  	 * closed down in order to release resources and object references.
83  	 */
84  	protected void stopHandler() { }
85  
86  	/***
87  	 * Start the socket data reader thread.
88  	 */
89  	protected void startHandler() {
90  		readerThread.start();
91  	}
92  	
93  	/***
94  	 * Checks for messages recieved from the server, and pass them to the
95  	 * message handler for further processing. Subclasses can override this
96  	 * method to provide their own message loop if message processing needs to
97  	 * be more fine grained than simple line based delimiting.
98  	 */
99  	protected void messageLoop() throws IOException {
100 		String message;
101 
102 		while (true) {
103 			message = in.readLine();
104 			log("Message: " + message);
105 
106 			if (message == null) {
107 				break;
108 			} else if (!messageHandler(message)) {
109 				break;
110 			}
111 		}
112 	}
113 	
114 	/***
115 	 * Invoked when a new message has been recieved. Sub-classes must implement
116 	 * this method to provide handing of recieved messages.
117 	 * 
118 	 * @param message received message from client
119 	 */
120 	protected abstract boolean messageHandler(String message);
121 	
122 	/***
123 	 * Get all routes from a specific user send them to the reciever.
124 	 * @param username username to get routes from
125 	 */
126 	protected void viewRoutes(String username) {
127 		List<Route> routes = db.getRoutes(username);
128 
129 		if (routes.size() == 0) {
130 			sendMessage(VIEW_ROUTES);
131 			return;
132 		}
133 		
134 		// Build output route string.
135 		StringBuilder routeString = new StringBuilder();
136 		for (Route route : routes) {
137 			makeMsg(DELIMITER, routeString, route.getRouteId(),
138 					route.getUserId(), route.getDescription(), route.isActive(),
139 					route.getLastUpdateString(), route.getVisible());
140 		}
141 //		GZIPOutputStream zipOutStream = new GZIPOutputStream(output);
142 //		zipOutStream.write(routeString.toString().getBytes());
143 		sendMessage(VIEW_ROUTES, routeString);
144 		
145 		log("Routes for user " + username + " has been sent");
146 	}
147 	
148 	/***
149 	 * Uses method in protocol to send message to client.
150 	 * 
151 	 * @param out output stream
152 	 * @param message fields
153 	 */
154 	protected void sendMessage(OutputStream out, Object... message) {
155 		try {
156 			send(out, message);
157 		} catch (IOException e) {
158 			log("Problem sending message to client: " + e);
159 			e.printStackTrace();
160 		}
161 	}
162 
163 	/***
164 	 * Sends a message to the client.
165 	 * 
166 	 * @param message message to send
167 	 */
168 	protected void sendMessage(Object... message) {
169 		sendMessage(out, message);
170 	}
171 
172 	/***
173 	 * Sends compressed data to client.
174 	 * 
175 	 * @param out output stream
176 	 * @param message fields
177 	 */
178 	protected void sendZipMessage(OutputStream out,	Object... message) {
179 		// TODO Implement ZIP compression method
180 	}
181 	
182 	/***
183 	 * This method is used for logging messages on the server.
184 	 * 
185 	 * @param message log message
186 	 */
187 	protected void log(String message) {
188 		server.log(user.getUsername(), message);
189 	}
190 	
191 	/***
192 	 * Returns the currently active route for this client handler.
193 	 * 
194 	 * @return currently active route
195 	 */
196 	protected Route getActiveRoute() {
197 		return route;
198 	}
199 	
200 	/***
201 	 * Inner class to receive incomming messages.
202 	 */
203 	private class DataReaderThread extends Thread {
204 		/***
205 		 * Start the message loop that receives messages from the socket.
206 		 */
207 		@Override
208 		public void run() {
209 			try {
210 				// Start message loop.
211 				messageLoop();
212 			} catch (IOException e) {
213 				log("Client terminated with IOException: " + e);
214 			} catch (RuntimeException e) {
215 				log("Client terminated with RuntimeException: " + e);
216 				e.printStackTrace();
217 			} finally {
218 				// Gracefully exit.
219 				// XXX Before or after socket.close()?
220 //				server.connectionUpdate(DISCONNECTED);
221 
222 				try {
223 					socket.close();
224 				} catch (IOException e) {
225 					// Broadcast error to clients.
226 //					server.socketErrorEvent(this, e);  // TODO Fix MVC.
227 					log("Error closing client socket: " + e);
228 					e.printStackTrace();
229 				}
230 				
231 				stopHandler();
232 				log("Client disconnected");
233 			}
234 		}
235 	}
236 }