10435e299d70febcdeb83f2f10086c41d85339c3
1 package be
.nikiroo
.utils
.serial
.server
;
3 import java
.io
.BufferedReader
;
4 import java
.io
.IOException
;
5 import java
.io
.InputStreamReader
;
6 import java
.io
.OutputStreamWriter
;
7 import java
.net
.Socket
;
9 import be
.nikiroo
.utils
.Version
;
10 import be
.nikiroo
.utils
.serial
.Exporter
;
11 import be
.nikiroo
.utils
.serial
.Importer
;
14 * Base class used for the client/server basic handling.
16 * It represents a single action: a client is expected to only execute one
17 * action, while a server is expected to execute one action for each client
22 abstract class ConnectAction
{
24 private boolean server
;
25 private Version version
;
26 private Version clientVersion
;
28 private Object lock
= new Object();
29 private BufferedReader in
;
30 private OutputStreamWriter out
;
31 private boolean contentToSend
;
34 * Method that will be called when an action is performed on either the
35 * client or server this {@link ConnectAction} represent.
38 * the counter part version
41 * in case of I/O error
43 abstract protected void action(Version version
) throws Exception
;
46 * Method called when we negotiate the version with the client.
48 * Thus, it is only called on the server.
50 * Will return the actual server version by default.
52 * @param clientVersion
55 * @return the version to send to the client
57 abstract protected Version
negotiateVersion(Version clientVersion
);
60 * Handler called when an unexpected error occurs in the code.
63 * the exception that occurred
65 abstract protected void onError(Exception e
);
68 * Create a new {@link ConnectAction}.
71 * the socket to bind to
73 * TRUE for a server action, FALSE for a client action (will
76 * the version of this client-or-server
78 protected ConnectAction(Socket s
, boolean server
, Version version
) {
82 if (version
== null) {
83 this.version
= new Version();
85 this.version
= version
;
88 clientVersion
= new Version();
92 * The version of this client-or-server.
96 public Version
getVersion() {
101 * Actually start the process (this is synchronous).
103 public void connect() {
105 in
= new BufferedReader(new InputStreamReader(s
.getInputStream(),
108 out
= new OutputStreamWriter(s
.getOutputStream(), "UTF-8");
111 String line
= in
.readLine();
112 if (line
!= null && line
.startsWith("VERSION ")) {
113 // "VERSION client-version" (VERSION 1.0.0)
114 Version clientVersion
= new Version(
115 line
.substring("VERSION ".length()));
116 this.clientVersion
= clientVersion
;
117 Version v
= negotiateVersion(clientVersion
);
122 sendString("VERSION " + v
.toString());
125 action(clientVersion
);
127 String v
= sendString("VERSION " + version
.toString());
128 if (v
!= null && v
.startsWith("VERSION ")) {
129 v
= v
.substring("VERSION ".length());
132 action(new Version(v
));
142 } catch (Exception e
) {
147 } catch (Exception e
) {
154 * Serialise and send the given object to the counter part (and, only for
155 * client, return the deserialised answer -- the server will always receive
161 * @return the answer (which can be NULL) if this action is a client, always
162 * NULL if it is a server
164 * @throws IOException
165 * in case of I/O error
166 * @throws NoSuchFieldException
167 * if the serialised data contains information about a field
168 * which does actually not exist in the class we know of
169 * @throws NoSuchMethodException
170 * if a class described in the serialised data cannot be created
171 * because it is not compatible with this code
172 * @throws ClassNotFoundException
173 * if a class described in the serialised data cannot be found
175 protected Object
sendObject(Object data
) throws IOException
,
176 NoSuchFieldException
, NoSuchMethodException
, ClassNotFoundException
{
177 synchronized (lock
) {
178 String rep
= sendString(new Exporter().append(data
).toString(true,
181 return new Importer().read(rep
).getValue();
189 * Reserved for the server: flush the data to the client and retrieve its
192 * Also used internally for the client (only do something if there is
195 * Will only flush the data if there is contentToSend.
197 * @return the deserialised answer (which can actually be NULL)
199 * @throws IOException
200 * in case of I/O error
201 * @throws NoSuchFieldException
202 * if the serialised data contains information about a field
203 * which does actually not exist in the class we know of
204 * @throws NoSuchMethodException
205 * if a class described in the serialised data cannot be created
206 * because it is not compatible with this code
207 * @throws ClassNotFoundException
208 * if a class described in the serialised data cannot be found
209 * @throws java.lang.NullPointerException
210 * if the counter part has no data to send
212 protected Object
recObject() throws IOException
, NoSuchFieldException
,
213 NoSuchMethodException
, ClassNotFoundException
,
214 java
.lang
.NullPointerException
{
215 String str
= recString();
217 throw new NullPointerException("No more data available");
220 return new Importer().read(str
).getValue();
224 * Send the given string to the counter part (and, only for client, return
225 * the answer -- the server will always receive NULL).
228 * the data to send (we will add a line feed)
230 * @return the answer if this action is a client (without the added line
231 * feed), NULL if it is a server
233 * @throws IOException
234 * in case of I/O error
236 protected String
sendString(String line
) throws IOException
{
237 synchronized (lock
) {
246 contentToSend
= true;
252 * Reserved for the server (externally): flush the data to the client and
253 * retrieve its answer.
255 * Also used internally for the client (only do something if there is
258 * Will only flush the data if there is contentToSend.
260 * @return the answer (which can be NULL)
262 * @throws IOException
263 * in case of I/O error
265 protected String
recString() throws IOException
{
266 synchronized (lock
) {
267 if (server
|| contentToSend
) {
270 contentToSend
= false;
273 return in
.readLine();