1 package be
.nikiroo
.utils
.serial
.server
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
5 import java
.io
.OutputStream
;
6 import java
.net
.Socket
;
8 import javax
.net
.ssl
.SSLException
;
10 import be
.nikiroo
.utils
.CryptUtils
;
11 import be
.nikiroo
.utils
.IOUtils
;
12 import be
.nikiroo
.utils
.StringUtils
;
13 import be
.nikiroo
.utils
.serial
.Exporter
;
14 import be
.nikiroo
.utils
.serial
.Importer
;
15 import be
.nikiroo
.utils
.streams
.BufferedOutputStream
;
16 import be
.nikiroo
.utils
.streams
.NextableInputStream
;
17 import be
.nikiroo
.utils
.streams
.NextableInputStreamStep
;
18 import be
.nikiroo
.utils
.streams
.ReplaceInputStream
;
19 import be
.nikiroo
.utils
.streams
.ReplaceOutputStream
;
22 * Base class used for the client/server basic handling.
24 * It represents a single action: a client is expected to only execute one
25 * action, while a server is expected to execute one action for each client
30 abstract class ConnectAction
{
31 // We separate each "packet" we send with this character and make sure it
32 // does not occurs in the message itself.
33 static private char STREAM_SEP
= '\b';
34 static private String
[] STREAM_RAW
= new String
[] { "\\", "\b" };
35 static private String
[] STREAM_CODED
= new String
[] { "\\\\", "\\b" };
38 private boolean server
;
40 private CryptUtils crypt
;
42 private Object lock
= new Object();
43 private NextableInputStream in
;
44 private BufferedOutputStream out
;
45 private boolean contentToSend
;
48 * Method that will be called when an action is performed on either the
49 * client or server this {@link ConnectAction} represent.
52 * in case of I/O error
54 abstract protected void action() throws Exception
;
57 * Handler called when an unexpected error occurs in the code.
60 * the exception that occurred, SSLException usually denotes a
63 abstract protected void onError(Exception e
);
66 * Create a new {@link ConnectAction}.
69 * the socket to bind to
71 * TRUE for a server action, FALSE for a client action (will
74 * an optional key to encrypt all the communications (if NULL,
75 * everything will be sent in clear text)
77 protected ConnectAction(Socket s
, boolean server
, String key
) {
81 crypt
= new CryptUtils(key
);
86 * The total amount of bytes received.
88 * @return the amount of bytes received
90 public long getBytesReceived() {
91 return in
.getBytesRead();
95 * The total amount of bytes sent.
97 * @return the amount of bytes sent
99 public long getBytesWritten() {
100 return out
.getBytesWritten();
104 * Actually start the process (this is synchronous).
106 public void connect() {
108 in
= new NextableInputStream(s
.getInputStream(),
109 new NextableInputStreamStep(STREAM_SEP
));
111 out
= new BufferedOutputStream(s
.getOutputStream());
120 } catch (Exception e
) {
125 } catch (Exception e
) {
132 * Serialise and send the given object to the counter part (and, only for
133 * client, return the deserialised answer -- the server will always receive
139 * @return the answer (which can be NULL if no answer, or NULL for an answer
140 * which is NULL) if this action is a client, always NULL if it is a
143 * @throws IOException
144 * in case of I/O error
145 * @throws NoSuchFieldException
146 * if the serialised data contains information about a field
147 * which does actually not exist in the class we know of
148 * @throws NoSuchMethodException
149 * if a class described in the serialised data cannot be created
150 * because it is not compatible with this code
151 * @throws ClassNotFoundException
152 * if a class described in the serialised data cannot be found
154 protected Object
sendObject(Object data
) throws IOException
,
155 NoSuchFieldException
, NoSuchMethodException
, ClassNotFoundException
{
156 return send(out
, data
, false);
160 * Reserved for the server: flush the data to the client and retrieve its
163 * Also used internally for the client (only do something if there is
166 * Will only flush the data if there is contentToSend.
168 * @return the deserialised answer (which can actually be NULL)
170 * @throws IOException
171 * in case of I/O error
172 * @throws NoSuchFieldException
173 * if the serialised data contains information about a field
174 * which does actually not exist in the class we know of
175 * @throws NoSuchMethodException
176 * if a class described in the serialised data cannot be created
177 * because it is not compatible with this code
178 * @throws ClassNotFoundException
179 * if a class described in the serialised data cannot be found
180 * @throws java.lang.NullPointerException
181 * if the counter part has no data to send
183 protected Object
recObject() throws IOException
, NoSuchFieldException
,
184 NoSuchMethodException
, ClassNotFoundException
,
185 java
.lang
.NullPointerException
{
190 * Send the given string to the counter part (and, only for client, return
191 * the answer -- the server will always receive NULL).
194 * the data to send (we will add a line feed)
196 * @return the answer if this action is a client (without the added line
197 * feed), NULL if it is a server
199 * @throws IOException
200 * in case of I/O error
201 * @throws SSLException
202 * in case of crypt error
204 protected String
sendString(String line
) throws IOException
{
206 return (String
) send(out
, line
, true);
207 } catch (NoSuchFieldException e
) {
210 } catch (NoSuchMethodException e
) {
213 } catch (ClassNotFoundException e
) {
222 * Reserved for the server (externally): flush the data to the client and
223 * retrieve its answer.
225 * Also used internally for the client (only do something if there is
228 * Will only flush the data if there is contentToSend.
230 * @return the answer (which can be NULL if no more content)
232 * @throws IOException
233 * in case of I/O error
234 * @throws SSLException
235 * in case of crypt error
237 protected String
recString() throws IOException
{
239 return (String
) rec(true);
240 } catch (NoSuchFieldException e
) {
243 } catch (NoSuchMethodException e
) {
246 } catch (ClassNotFoundException e
) {
249 } catch (NullPointerException e
) {
258 * Serialise and send the given object to the counter part (and, only for
259 * client, return the deserialised answer -- the server will always receive
263 * the stream to write to
267 * TRUE to write it as a String, FALSE to write it as an Object
269 * @return the answer (which can be NULL if no answer, or NULL for an answer
270 * which is NULL) if this action is a client, always NULL if it is a
273 * @throws IOException
274 * in case of I/O error
275 * @throws SSLException
276 * in case of crypt error
277 * @throws IOException
278 * in case of I/O error
279 * @throws NoSuchFieldException
280 * if the serialised data contains information about a field
281 * which does actually not exist in the class we know of
282 * @throws NoSuchMethodException
283 * if a class described in the serialised data cannot be created
284 * because it is not compatible with this code
285 * @throws ClassNotFoundException
286 * if a class described in the serialised data cannot be found
288 private Object
send(BufferedOutputStream out
, Object data
, boolean asString
)
289 throws IOException
, NoSuchFieldException
, NoSuchMethodException
,
290 ClassNotFoundException
, java
.lang
.NullPointerException
{
292 synchronized (lock
) {
295 sub
= crypt
.encrypt64(out
.open());
300 sub
= new ReplaceOutputStream(sub
, STREAM_RAW
, STREAM_CODED
);
303 sub
.write(StringUtils
.getBytes(data
.toString()));
305 new Exporter(sub
).append(data
);
311 out
.write(STREAM_SEP
);
318 contentToSend
= true;
320 return rec(asString
);
321 } catch (NullPointerException e
) {
322 // We accept no data here for Objects
330 * Reserved for the server: flush the data to the client and retrieve its
333 * Also used internally for the client (only do something if there is
336 * Will only flush the data if there is contentToSend.
338 * Note that the behaviour is slightly different for String and Object
339 * reading regarding exceptions:
341 * <li>NULL means that the counter part has no more data to send</li>
342 * <li>All the exceptions except {@link IOException} are there for Object
347 * TRUE for String reading, FALSE for Object reading (which can
350 * @return the deserialised answer (which can actually be NULL)
352 * @throws IOException
353 * in case of I/O error
354 * @throws NoSuchFieldException
355 * if the serialised data contains information about a field
356 * which does actually not exist in the class we know of
357 * @throws NoSuchMethodException
358 * if a class described in the serialised data cannot be created
359 * because it is not compatible with this code
360 * @throws ClassNotFoundException
361 * if a class described in the serialised data cannot be found
362 * @throws java.lang.NullPointerException
363 * for Objects only: if the counter part has no data to send
365 @SuppressWarnings("resource")
366 private Object
rec(boolean asString
) throws IOException
,
367 NoSuchFieldException
, NoSuchMethodException
,
368 ClassNotFoundException
, java
.lang
.NullPointerException
{
370 synchronized (lock
) {
371 if (server
|| contentToSend
) {
374 contentToSend
= false;
377 if (in
.next() && !in
.eof()) {
378 InputStream read
= new ReplaceInputStream(in
.open(),
379 STREAM_CODED
, STREAM_RAW
);
382 read
= crypt
.decrypt64(read
);
386 return IOUtils
.readSmallStream(read
);
389 return new Importer().read(read
).getValue();
396 throw new NullPointerException();