aafad62facfa5a3a3433df3a7aa54de8f23806b4
1 package be
.nikiroo
.utils
.serial
.server
;
3 import java
.io
.IOException
;
4 import java
.io
.OutputStream
;
5 import java
.net
.Socket
;
7 import javax
.net
.ssl
.SSLException
;
9 import be
.nikiroo
.utils
.CryptUtils
;
10 import be
.nikiroo
.utils
.IOUtils
;
11 import be
.nikiroo
.utils
.serial
.Exporter
;
12 import be
.nikiroo
.utils
.serial
.Importer
;
13 import be
.nikiroo
.utils
.streams
.NextableInputStream
;
14 import be
.nikiroo
.utils
.streams
.NextableInputStreamStep
;
17 * Base class used for the client/server basic handling.
19 * It represents a single action: a client is expected to only execute one
20 * action, while a server is expected to execute one action for each client
25 abstract class ConnectAction
{
27 private boolean server
;
29 private CryptUtils crypt
;
31 private Object lock
= new Object();
32 private NextableInputStream in
;
33 private OutputStream out
;
34 private boolean contentToSend
;
36 private long bytesReceived
;
37 private long bytesSent
;
40 * Method that will be called when an action is performed on either the
41 * client or server this {@link ConnectAction} represent.
44 * in case of I/O error
46 abstract protected void action() throws Exception
;
49 * Handler called when an unexpected error occurs in the code.
52 * the exception that occurred, SSLException usually denotes a
55 abstract protected void onError(Exception e
);
58 * Create a new {@link ConnectAction}.
61 * the socket to bind to
63 * TRUE for a server action, FALSE for a client action (will
66 * an optional key to encrypt all the communications (if NULL,
67 * everything will be sent in clear text)
69 protected ConnectAction(Socket s
, boolean server
, String key
) {
73 crypt
= new CryptUtils(key
);
78 * The total amount of bytes received.
80 * @return the amount of bytes received
82 public long getBytesReceived() {
87 * The total amount of bytes sent.
89 * @return the amount of bytes sent
91 public long getBytesSent() {
96 * Actually start the process (this is synchronous).
98 public void connect() {
100 in
= new NextableInputStream(s
.getInputStream(),
101 new NextableInputStreamStep('\b'));
104 out
= s
.getOutputStream();
115 } catch (Exception e
) {
120 } catch (Exception e
) {
127 * Serialise and send the given object to the counter part (and, only for
128 * client, return the deserialised answer -- the server will always receive
134 * @return the answer (which can be NULL if no answer, or NULL for an answer
135 * which is NULL) if this action is a client, always NULL if it is a
138 * @throws IOException
139 * in case of I/O error
140 * @throws NoSuchFieldException
141 * if the serialised data contains information about a field
142 * which does actually not exist in the class we know of
143 * @throws NoSuchMethodException
144 * if a class described in the serialised data cannot be created
145 * because it is not compatible with this code
146 * @throws ClassNotFoundException
147 * if a class described in the serialised data cannot be found
149 protected Object
sendObject(Object data
) throws IOException
,
150 NoSuchFieldException
, NoSuchMethodException
, ClassNotFoundException
{
151 synchronized (lock
) {
153 new Exporter(out
).append(data
);
161 contentToSend
= true;
164 } catch (NullPointerException e
) {
165 // We accept no data here
173 * Reserved for the server: flush the data to the client and retrieve its
176 * Also used internally for the client (only do something if there is
179 * Will only flush the data if there is contentToSend.
181 * @return the deserialised answer (which can actually be NULL)
183 * @throws IOException
184 * in case of I/O error
185 * @throws NoSuchFieldException
186 * if the serialised data contains information about a field
187 * which does actually not exist in the class we know of
188 * @throws NoSuchMethodException
189 * if a class described in the serialised data cannot be created
190 * because it is not compatible with this code
191 * @throws ClassNotFoundException
192 * if a class described in the serialised data cannot be found
193 * @throws java.lang.NullPointerException
194 * if the counter part has no data to send
196 protected Object
recObject() throws IOException
, NoSuchFieldException
,
197 NoSuchMethodException
, ClassNotFoundException
,
198 java
.lang
.NullPointerException
{
199 synchronized (lock
) {
200 if (server
|| contentToSend
) {
203 contentToSend
= false;
207 return new Importer().read(in
).getValue();
210 throw new NullPointerException();
218 * Send the given string to the counter part (and, only for client, return
219 * the answer -- the server will always receive NULL).
222 * the data to send (we will add a line feed)
224 * @return the answer if this action is a client (without the added line
225 * feed), NULL if it is a server
227 * @throws IOException
228 * in case of I/O error
229 * @throws SSLException
230 * in case of crypt error
232 protected String
sendString(String line
) throws IOException
{
233 synchronized (lock
) {
234 writeLine(out
, line
);
241 contentToSend
= true;
247 * Reserved for the server (externally): flush the data to the client and
248 * retrieve its answer.
250 * Also used internally for the client (only do something if there is
253 * Will only flush the data if there is contentToSend.
255 * @return the answer (which can be NULL)
257 * @throws IOException
258 * in case of I/O error
259 * @throws SSLException
260 * in case of crypt error
262 protected String
recString() throws IOException
{
263 synchronized (lock
) {
264 if (server
|| contentToSend
) {
267 contentToSend
= false;
278 * Read a possibly encrypted line.
281 * the stream to read from
282 * @return the unencrypted line
285 * @throws IOException
286 * in case of I/O error
287 * @throws SSLException
288 * in case of crypt error
290 private String
readLine(NextableInputStream in
) throws IOException
{
293 line
= IOUtils
.readSmallStream(in
);
297 bytesReceived
+= line
.length();
299 line
= crypt
.decrypt64s(line
, false);
307 * Write a line, possible encrypted.
310 * the stream to write to
313 * @throws IOException
314 * in case of I/O error
315 * @throws SSLException
316 * in case of crypt error
318 private void writeLine(OutputStream out
, String line
) throws IOException
{
320 out
.write(line
.getBytes("UTF-8"));
321 bytesSent
+= line
.length();
323 // TODO: how NOT to create so many big Strings?
324 String b64
= crypt
.encrypt64(line
, false);
325 out
.write(b64
.getBytes("UTF-8"));
326 bytesSent
+= b64
.length();