50d9ffcf61abdc8991094769ab8b6fd3cf3d68aa
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
.serial
.Exporter
;
13 import be
.nikiroo
.utils
.serial
.Importer
;
14 import be
.nikiroo
.utils
.streams
.BufferedOutputStream
;
15 import be
.nikiroo
.utils
.streams
.NextableInputStream
;
16 import be
.nikiroo
.utils
.streams
.NextableInputStreamStep
;
17 import be
.nikiroo
.utils
.streams
.ReplaceInputStream
;
18 import be
.nikiroo
.utils
.streams
.ReplaceOutputStream
;
21 * Base class used for the client/server basic handling.
23 * It represents a single action: a client is expected to only execute one
24 * action, while a server is expected to execute one action for each client
29 abstract class ConnectAction
{
31 private boolean server
;
33 private CryptUtils crypt
;
35 private Object lock
= new Object();
36 private NextableInputStream in
;
37 private BufferedOutputStream out
;
38 private boolean contentToSend
;
41 * Method that will be called when an action is performed on either the
42 * client or server this {@link ConnectAction} represent.
45 * in case of I/O error
47 abstract protected void action() throws Exception
;
50 * Handler called when an unexpected error occurs in the code.
53 * the exception that occurred, SSLException usually denotes a
56 abstract protected void onError(Exception e
);
59 * Create a new {@link ConnectAction}.
62 * the socket to bind to
64 * TRUE for a server action, FALSE for a client action (will
67 * an optional key to encrypt all the communications (if NULL,
68 * everything will be sent in clear text)
70 protected ConnectAction(Socket s
, boolean server
, String key
) {
74 crypt
= new CryptUtils(key
);
79 * The total amount of bytes received.
81 * @return the amount of bytes received
83 public long getBytesReceived() {
84 return in
.getBytesRead();
88 * The total amount of bytes sent.
90 * @return the amount of bytes sent
92 public long getBytesWritten() {
93 return out
.getBytesWritten();
97 * Actually start the process (this is synchronous).
99 public void connect() {
101 // TODO: assure that \b is never used, make sure \n usage is OK
102 in
= new NextableInputStream(s
.getInputStream(),
103 new NextableInputStreamStep('\b'));
106 out
= new BufferedOutputStream(s
.getOutputStream());
116 } catch (Exception e
) {
121 } catch (Exception e
) {
128 * Serialise and send the given object to the counter part (and, only for
129 * client, return the deserialised answer -- the server will always receive
135 * @return the answer (which can be NULL if no answer, or NULL for an answer
136 * which is NULL) if this action is a client, always NULL if it is a
139 * @throws IOException
140 * in case of I/O error
141 * @throws NoSuchFieldException
142 * if the serialised data contains information about a field
143 * which does actually not exist in the class we know of
144 * @throws NoSuchMethodException
145 * if a class described in the serialised data cannot be created
146 * because it is not compatible with this code
147 * @throws ClassNotFoundException
148 * if a class described in the serialised data cannot be found
150 protected Object
sendObject(Object data
) throws IOException
,
151 NoSuchFieldException
, NoSuchMethodException
, ClassNotFoundException
{
152 return send(out
, data
, false);
156 * Reserved for the server: flush the data to the client and retrieve its
159 * Also used internally for the client (only do something if there is
162 * Will only flush the data if there is contentToSend.
164 * @return the deserialised answer (which can actually be NULL)
166 * @throws IOException
167 * in case of I/O error
168 * @throws NoSuchFieldException
169 * if the serialised data contains information about a field
170 * which does actually not exist in the class we know of
171 * @throws NoSuchMethodException
172 * if a class described in the serialised data cannot be created
173 * because it is not compatible with this code
174 * @throws ClassNotFoundException
175 * if a class described in the serialised data cannot be found
176 * @throws java.lang.NullPointerException
177 * if the counter part has no data to send
179 protected Object
recObject() throws IOException
, NoSuchFieldException
,
180 NoSuchMethodException
, ClassNotFoundException
,
181 java
.lang
.NullPointerException
{
186 * Send the given string to the counter part (and, only for client, return
187 * the answer -- the server will always receive NULL).
190 * the data to send (we will add a line feed)
192 * @return the answer if this action is a client (without the added line
193 * feed), NULL if it is a server
195 * @throws IOException
196 * in case of I/O error
197 * @throws SSLException
198 * in case of crypt error
200 protected String
sendString(String line
) throws IOException
{
202 return (String
) send(out
, line
, true);
203 } catch (NoSuchFieldException e
) {
206 } catch (NoSuchMethodException e
) {
209 } catch (ClassNotFoundException e
) {
218 * Reserved for the server (externally): flush the data to the client and
219 * retrieve its answer.
221 * Also used internally for the client (only do something if there is
224 * Will only flush the data if there is contentToSend.
226 * @return the answer (which can be NULL if no more content)
228 * @throws IOException
229 * in case of I/O error
230 * @throws SSLException
231 * in case of crypt error
233 protected String
recString() throws IOException
{
235 return (String
) rec(true);
236 } catch (NoSuchFieldException e
) {
239 } catch (NoSuchMethodException e
) {
242 } catch (ClassNotFoundException e
) {
245 } catch (NullPointerException e
) {
254 * Serialise and send the given object to the counter part (and, only for
255 * client, return the deserialised answer -- the server will always receive
259 * the stream to write to
263 * TRUE to write it as a String, FALSE to write it as an Object
265 * @return the answer (which can be NULL if no answer, or NULL for an answer
266 * which is NULL) if this action is a client, always NULL if it is a
269 * @throws IOException
270 * in case of I/O error
271 * @throws SSLException
272 * in case of crypt error
273 * @throws IOException
274 * in case of I/O error
275 * @throws NoSuchFieldException
276 * if the serialised data contains information about a field
277 * which does actually not exist in the class we know of
278 * @throws NoSuchMethodException
279 * if a class described in the serialised data cannot be created
280 * because it is not compatible with this code
281 * @throws ClassNotFoundException
282 * if a class described in the serialised data cannot be found
284 private Object
send(BufferedOutputStream out
, Object data
, boolean asString
)
285 throws IOException
, NoSuchFieldException
, NoSuchMethodException
,
286 ClassNotFoundException
, java
.lang
.NullPointerException
{
288 synchronized (lock
) {
291 sub
= crypt
.encrypt64(out
.open());
296 // TODO: could be possible to check for non-crypt and only
298 sub
= new ReplaceOutputStream(sub
, //
299 new String
[] { "\\", "\b" }, //
300 new String
[] { "\\\\", "\\b" });
304 sub
.write(data
.toString().getBytes("UTF-8"));
306 new Exporter(sub
).append(data
);
319 contentToSend
= true;
321 return rec(asString
);
322 } catch (NullPointerException e
) {
323 // We accept no data here for Objects
331 * Reserved for the server: flush the data to the client and retrieve its
334 * Also used internally for the client (only do something if there is
337 * Will only flush the data if there is contentToSend.
339 * Note that the behaviour is slightly different for String and Object
340 * reading regarding exceptions:
342 * <li>NULL means that the counter part has no more data to send</li>
343 * <li>All the exceptions except {@link IOException} are there for Object
348 * TRUE for String reading, FALSE for Object reading (which can
351 * @return the deserialised answer (which can actually be NULL)
353 * @throws IOException
354 * in case of I/O error
355 * @throws NoSuchFieldException
356 * if the serialised data contains information about a field
357 * which does actually not exist in the class we know of
358 * @throws NoSuchMethodException
359 * if a class described in the serialised data cannot be created
360 * because it is not compatible with this code
361 * @throws ClassNotFoundException
362 * if a class described in the serialised data cannot be found
363 * @throws java.lang.NullPointerException
364 * for Objects only: if the counter part has no data to send
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;
378 // TODO: could be possible to check for non-crypt and only
380 InputStream read
= new ReplaceInputStream(in
.open(), //
381 new String
[] { "\\\\", "\\b" }, //
382 new String
[] { "\\", "\b" });
386 read
= crypt
.decrypt64(read
);
390 return IOUtils
.readSmallStream(read
);
393 return new Importer().read(read
).getValue();
400 throw new NullPointerException();