-
Notifications
You must be signed in to change notification settings - Fork 100
Expand file tree
/
Copy pathAbstractClient.java
More file actions
336 lines (287 loc) · 8.3 KB
/
AbstractClient.java
File metadata and controls
336 lines (287 loc) · 8.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
// This file contains material supporting section 3.7 of the textbook:
// "Object Oriented Software Engineering" and is issued under the open-source
// license found at www.lloseng.com
package ocsf.client;
import java.io.*;
import java.net.*;
/**
* The <code> AbstractClient </code> contains all the methods necessary to set
* up the client side of a client-server architecture. When a client is thus
* connected to the server, the two programs can then exchange
* <code> Object </code> instances.
* <p>
* Method <code> handleMessageFromServer </code> must be defined by a concrete
* subclass. Several other hook methods may also be overriden.
* <p>
* Several public service methods are provided to application that use this
* framework.
* <p>
* Project Name: OCSF (Object Client-Server Framework)
* <p>
*
* @author Dr. Robert Laganière
* @author Dr. Timothy C. Lethbridge
* @author François Bél;langer
* @author Paul Holden
* @version February 2001 (2.12)
*/
public abstract class AbstractClient implements Runnable {
// INSTANCE VARIABLES ***********************************************
/**
* Sockets are used in the operating system as channels of communication
* between two processes.
*
* @see java.net.Socket
*/
private Socket clientSocket;
/**
* The stream to handle data going to the server.
*/
private ObjectOutputStream output;
/**
* The stream to handle data from the server.
*/
private ObjectInputStream input;
/**
* The thread created to read data from the server.
*/
private Thread clientReader;
/**
* Indicates if the thread is ready to stop. Needed so that the loop in the
* run method knows when to stop waiting for incoming messages.
*/
private boolean readyToStop = false;
/**
* The server's host name.
*/
private String host;
/**
* The port number.
*/
private int port;
// CONSTRUCTORS *****************************************************
/**
* Constructs the client.
*
* @param host
* the server's host name.
* @param port
* the port number.
*/
public AbstractClient(String host, int port) {
// Initialize variables
this.host = host;
this.port = port;
}
// INSTANCE METHODS *************************************************
/**
* Opens the connection with the server. If the connection is already
* opened, this call has no effect.
*
* @exception IOException
* if an I/O error occurs when opening.
*/
final public void openConnection() throws IOException {
// Do not do anything if the connection is already open
if (isConnected())
return;
// Create the sockets and the data streams
try {
clientSocket = new Socket(host, port);
output = new ObjectOutputStream(clientSocket.getOutputStream());
input = new ObjectInputStream(clientSocket.getInputStream());
} catch (IOException ex)
// All three of the above must be closed when there is a failure
// to create any of them
{
try {
closeAll();
} catch (Exception exc) {
}
throw ex; // Rethrow the exception.
}
clientReader = new Thread(this); // Create the data reader thread
readyToStop = false;
clientReader.start(); // Start the thread
}
/**
* Sends an object to the server. This is the only way that methods should
* communicate with the server.
*
* @param msg
* The message to be sent.
* @exception IOException
* if an I/O error occurs when sending
*/
final public void sendToServer(Object msg) throws IOException {
if (clientSocket == null || output == null)
throw new SocketException("socket does not exist");
output.writeObject(msg);
}
/**
* Reset the object output stream so we can use the same
* buffer repeatedly. This would not normally be used, but is necessary
* in some circumstances when Java refuses to send data that it thinks has been sent.
*/
final public void forceResetAfterSend() throws IOException {
output.reset();
}
/**
* Closes the connection to the server.
*
* @exception IOException
* if an I/O error occurs when closing.
*/
final public void closeConnection() throws IOException {
// Prevent the thread from looping any more
readyToStop = true;
try {
closeAll();
} finally {
// Call the hook method
connectionClosed();
}
}
// ACCESSING METHODS ------------------------------------------------
/**
* @return true if the client is connnected.
*/
final public boolean isConnected() {
return clientReader != null && clientReader.isAlive();
}
/**
* @return the port number.
*/
final public int getPort() {
return port;
}
/**
* Sets the server port number for the next connection. The change in port
* only takes effect at the time of the next call to openConnection().
*
* @param port
* the port number.
*/
final public void setPort(int port) {
this.port = port;
}
/**
* @return the host name.
*/
final public String getHost() {
return host;
}
/**
* Sets the server host for the next connection. The change in host only
* takes effect at the time of the next call to openConnection().
*
* @param host
* the host name.
*/
final public void setHost(String host) {
this.host = host;
}
/**
* returns the client's description.
*
* @return the client's Inet address.
*/
final public InetAddress getInetAddress() {
return clientSocket.getInetAddress();
}
// RUN METHOD -------------------------------------------------------
/**
* Waits for messages from the server. When each arrives, a call is made to
* <code>handleMessageFromServer()</code>. Not to be explicitly called.
*/
final public void run() {
connectionEstablished();
// The message from the server
Object msg;
// Loop waiting for data
try {
while (!readyToStop) {
// Get data from Server and send it to the handler
// The thread waits indefinitely at the following
// statement until something is received from the server
msg = input.readObject();
// Concrete subclasses do what they want with the
// msg by implementing the following method
handleMessageFromServer(msg);
}
} catch (Exception exception) {
if (!readyToStop) {
try {
closeAll();
} catch (Exception ex) {
}
connectionException(exception);
}
} finally {
clientReader = null;
}
}
// METHODS DESIGNED TO BE OVERRIDDEN BY CONCRETE SUBCLASSES ---------
/**
* Hook method called after the connection has been closed. The default
* implementation does nothing. The method may be overriden by subclasses to
* perform special processing such as cleaning up and terminating, or
* attempting to reconnect.
*/
protected void connectionClosed() {
}
/**
* Hook method called each time an exception is thrown by the client's
* thread that is waiting for messages from the server. The method may be
* overridden by subclasses.
*
* @param exception
* the exception raised.
*/
protected void connectionException(Exception exception) {
}
/**
* Hook method called after a connection has been established. The default
* implementation does nothing. It may be overridden by subclasses to do
* anything they wish.
*/
protected void connectionEstablished() {
}
/**
* Handles a message sent from the server to this client. This MUST be
* implemented by subclasses, who should respond to messages.
*
* @param msg
* the message sent.
*/
protected abstract void handleMessageFromServer(Object msg);
// METHODS TO BE USED FROM WITHIN THE FRAMEWORK ONLY ----------------
/**
* Closes all aspects of the connection to the server.
*
* @exception IOException
* if an I/O error occurs when closing.
*/
private void closeAll() throws IOException {
try {
// Close the socket
if (clientSocket != null)
clientSocket.close();
// Close the output stream
if (output != null)
output.close();
// Close the input stream
if (input != null)
input.close();
} finally {
// Set the streams and the sockets to NULL no matter what
// Doing so allows, but does not require, any finalizers
// of these objects to reclaim system resources if and
// when they are garbage collected.
output = null;
input = null;
clientSocket = null;
}
}
}
// end of AbstractClient class