1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
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
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
142
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
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
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
219
220
221
222 try {
223 socket.close();
224 } catch (IOException e) {
225
226
227 log("Error closing client socket: " + e);
228 e.printStackTrace();
229 }
230
231 stopHandler();
232 log("Client disconnected");
233 }
234 }
235 }
236 }