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
.Version
;
14 import be
.nikiroo
.utils
.serial
.Exporter
;
15 import be
.nikiroo
.utils
.serial
.Importer
;
16 import be
.nikiroo
.utils
.streams
.BufferedOutputStream
;
17 import be
.nikiroo
.utils
.streams
.NextableInputStream
;
18 import be
.nikiroo
.utils
.streams
.NextableInputStreamStep
;
19 import be
.nikiroo
.utils
.streams
.ReplaceInputStream
;
20 import be
.nikiroo
.utils
.streams
.ReplaceOutputStream
;
23 * Base class used for the client/server basic handling.
25 * It represents a single action: a client is expected to only execute one
26 * action, while a server is expected to execute one action for each client
31 abstract class ConnectAction
{
32 // We separate each "packet" we send with this character and make sure it
33 // does not occurs in the message itself.
34 static private char STREAM_SEP
= '\b';
35 static private String
[] STREAM_RAW
= new String
[] { "\\", "\b" };
36 static private String
[] STREAM_CODED
= new String
[] { "\\\\", "\\b" };
39 private boolean server
;
41 private Version clientVersion
;
42 private Version serverVersion
;
44 private CryptUtils crypt
;
46 private Object lock
= new Object();
47 private NextableInputStream in
;
48 private BufferedOutputStream out
;
49 private boolean contentToSend
;
52 * Method that will be called when an action is performed on either the
53 * client or server this {@link ConnectAction} represent.
56 * the version on the other side of the communication (client or
60 * in case of I/O error
62 abstract protected void action(Version version
) throws Exception
;
65 * Method called when we negotiate the version with the client.
67 * Thus, it is only called on the server.
69 * Will return the actual server version by default.
71 * @param clientVersion
74 * @return the version to send to the client
76 abstract protected Version
negotiateVersion(Version clientVersion
);
79 * Handler called when an unexpected error occurs in the code.
82 * the exception that occurred, SSLException usually denotes a
85 abstract protected void onError(Exception e
);
88 * Create a new {@link ConnectAction}.
91 * the socket to bind to
93 * TRUE for a server action, FALSE for a client action (will
96 * an optional key to encrypt all the communications (if NULL,
97 * everything will be sent in clear text)
99 * the client-or-server version (depending upon the boolean
100 * parameter <tt>server</tt>)
102 protected ConnectAction(Socket s
, boolean server
, String key
,
105 this.server
= server
;
107 crypt
= new CryptUtils(key
);
110 if (version
== null) {
111 version
= new Version();
115 serverVersion
= version
;
117 clientVersion
= version
;
122 * The version of this client-or-server.
124 * @return the version
126 public Version
getVersion() {
128 return serverVersion
;
131 return clientVersion
;
135 * The total amount of bytes received.
137 * @return the amount of bytes received
139 public long getBytesReceived() {
140 return in
.getBytesRead();
144 * The total amount of bytes sent.
146 * @return the amount of bytes sent
148 public long getBytesWritten() {
149 return out
.getBytesWritten();
153 * Actually start the process (this is synchronous).
155 public void connect() {
157 in
= new NextableInputStream(s
.getInputStream(),
158 new NextableInputStreamStep(STREAM_SEP
));
160 out
= new BufferedOutputStream(s
.getOutputStream());
162 action(server ? serverVersion
: clientVersion
);
169 } catch (Exception e
) {
174 } catch (Exception e
) {
181 * Serialise and send the given object to the counter part (and, only for
182 * client, return the deserialised answer -- the server will always receive
188 * @return the answer (which can be NULL if no answer, or NULL for an answer
189 * which is NULL) if this action is a client, always NULL if it is a
192 * @throws IOException
193 * in case of I/O error
194 * @throws NoSuchFieldException
195 * if the serialised data contains information about a field
196 * which does actually not exist in the class we know of
197 * @throws NoSuchMethodException
198 * if a class described in the serialised data cannot be created
199 * because it is not compatible with this code
200 * @throws ClassNotFoundException
201 * if a class described in the serialised data cannot be found
203 protected Object
sendObject(Object data
) throws IOException
,
204 NoSuchFieldException
, NoSuchMethodException
, ClassNotFoundException
{
205 return send(out
, data
, false);
209 * Reserved for the server: flush the data to the client and retrieve its
212 * Also used internally for the client (only do something if there is
215 * Will only flush the data if there is contentToSend.
217 * @return the deserialised answer (which can actually be NULL)
219 * @throws IOException
220 * in case of I/O error
221 * @throws NoSuchFieldException
222 * if the serialised data contains information about a field
223 * which does actually not exist in the class we know of
224 * @throws NoSuchMethodException
225 * if a class described in the serialised data cannot be created
226 * because it is not compatible with this code
227 * @throws ClassNotFoundException
228 * if a class described in the serialised data cannot be found
229 * @throws java.lang.NullPointerException
230 * if the counter part has no data to send
232 protected Object
recObject() throws IOException
, NoSuchFieldException
,
233 NoSuchMethodException
, ClassNotFoundException
,
234 java
.lang
.NullPointerException
{
239 * Send the given string to the counter part (and, only for client, return
240 * the answer -- the server will always receive NULL).
243 * the data to send (we will add a line feed)
245 * @return the answer if this action is a client (without the added line
246 * feed), NULL if it is a server
248 * @throws IOException
249 * in case of I/O error
250 * @throws SSLException
251 * in case of crypt error
253 protected String
sendString(String line
) throws IOException
{
255 return (String
) send(out
, line
, true);
256 } catch (NoSuchFieldException e
) {
259 } catch (NoSuchMethodException e
) {
262 } catch (ClassNotFoundException e
) {
271 * Reserved for the server (externally): flush the data to the client and
272 * retrieve its answer.
274 * Also used internally for the client (only do something if there is
277 * Will only flush the data if there is contentToSend.
279 * @return the answer (which can be NULL if no more content)
281 * @throws IOException
282 * in case of I/O error
283 * @throws SSLException
284 * in case of crypt error
286 protected String
recString() throws IOException
{
288 return (String
) rec(true);
289 } catch (NoSuchFieldException e
) {
292 } catch (NoSuchMethodException e
) {
295 } catch (ClassNotFoundException e
) {
298 } catch (NullPointerException e
) {
307 * Serialise and send the given object to the counter part (and, only for
308 * client, return the deserialised answer -- the server will always receive
312 * the stream to write to
316 * TRUE to write it as a String, FALSE to write it as an Object
318 * @return the answer (which can be NULL if no answer, or NULL for an answer
319 * which is NULL) if this action is a client, always NULL if it is a
322 * @throws IOException
323 * in case of I/O error
324 * @throws SSLException
325 * in case of crypt error
326 * @throws IOException
327 * in case of I/O error
328 * @throws NoSuchFieldException
329 * if the serialised data contains information about a field
330 * which does actually not exist in the class we know of
331 * @throws NoSuchMethodException
332 * if a class described in the serialised data cannot be created
333 * because it is not compatible with this code
334 * @throws ClassNotFoundException
335 * if a class described in the serialised data cannot be found
337 private Object
send(BufferedOutputStream out
, Object data
, boolean asString
)
338 throws IOException
, NoSuchFieldException
, NoSuchMethodException
,
339 ClassNotFoundException
, java
.lang
.NullPointerException
{
341 synchronized (lock
) {
344 sub
= crypt
.encrypt64(out
.open());
349 sub
= new ReplaceOutputStream(sub
, STREAM_RAW
, STREAM_CODED
);
352 sub
.write(StringUtils
.getBytes(data
.toString()));
354 new Exporter(sub
).append(data
);
360 out
.write(STREAM_SEP
);
367 contentToSend
= true;
369 return rec(asString
);
370 } catch (NullPointerException e
) {
371 // We accept no data here for Objects
379 * Reserved for the server: flush the data to the client and retrieve its
382 * Also used internally for the client (only do something if there is
385 * Will only flush the data if there is contentToSend.
387 * Note that the behaviour is slightly different for String and Object
388 * reading regarding exceptions:
390 * <li>NULL means that the counter part has no more data to send</li>
391 * <li>All the exceptions except {@link IOException} are there for Object
396 * TRUE for String reading, FALSE for Object reading (which can
399 * @return the deserialised answer (which can actually be NULL)
401 * @throws IOException
402 * in case of I/O error
403 * @throws NoSuchFieldException
404 * if the serialised data contains information about a field
405 * which does actually not exist in the class we know of
406 * @throws NoSuchMethodException
407 * if a class described in the serialised data cannot be created
408 * because it is not compatible with this code
409 * @throws ClassNotFoundException
410 * if a class described in the serialised data cannot be found
411 * @throws java.lang.NullPointerException
412 * for Objects only: if the counter part has no data to send
414 @SuppressWarnings("resource")
415 private Object
rec(boolean asString
) throws IOException
,
416 NoSuchFieldException
, NoSuchMethodException
,
417 ClassNotFoundException
, java
.lang
.NullPointerException
{
419 synchronized (lock
) {
420 if (server
|| contentToSend
) {
423 contentToSend
= false;
426 if (in
.next() && !in
.eof()) {
427 InputStream read
= new ReplaceInputStream(in
.open(),
428 STREAM_CODED
, STREAM_RAW
);
431 read
= crypt
.decrypt64(read
);
435 return IOUtils
.readSmallStream(read
);
438 return new Importer().read(read
).getValue();
445 throw new NullPointerException();