dfc1a2bb422138ea8acbf930a831d0c6d7a7a0da
1 package be
.nikiroo
.utils
.serial
;
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
;
12 * Base class used for the client/server basic handling.
14 * It represents a single action: a client is expected to only execute one
15 * action, while a server is expected to execute one action for each client
20 abstract class ConnectAction
{
22 private boolean server
;
23 private Version version
;
24 private Version clientVersion
;
26 private Object lock
= new Object();
27 private BufferedReader in
;
28 private OutputStreamWriter out
;
29 private boolean contentToSend
;
32 * Method that will be called when an action is performed on either the
33 * client or server this {@link ConnectAction} represent.
36 * the counter part version
39 * in case of I/O error
41 abstract protected void action(Version version
) throws Exception
;
44 * Method called when we negotiate the version with the client.
46 * Thus, it is only called on the server.
48 * Will return the actual server version by default.
50 * @param clientVersion
53 * @return the version to send to the client
55 abstract protected Version
negotiateVersion(Version clientVersion
);
58 * Handler called when an unexpected error occurs in the code.
61 * the exception that occurred
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 * the version of this client-or-server
76 protected ConnectAction(Socket s
, boolean server
, Version version
) {
80 if (version
== null) {
81 this.version
= new Version();
83 this.version
= version
;
86 clientVersion
= new Version();
90 * The version of this client-or-server.
94 public Version
getVersion() {
99 * Actually start the process (this is synchronous).
101 public void connect() {
103 in
= new BufferedReader(new InputStreamReader(s
.getInputStream(),
106 out
= new OutputStreamWriter(s
.getOutputStream(), "UTF-8");
109 action(clientVersion
);
111 String v
= sendString("VERSION " + version
.toString());
112 if (v
!= null && v
.startsWith("VERSION ")) {
113 v
= v
.substring("VERSION ".length());
116 action(new Version(v
));
124 } catch (Exception e
) {
129 } catch (Exception e
) {
136 * Serialise and send the given object to the counter part (and, only for
137 * client, return the deserialised answer -- the server will always receive
143 * @return the answer (which can be NULL) if this action is a client, always
144 * NULL if it is a server
146 * @throws IOException
147 * in case of I/O error
148 * @throws NoSuchFieldException
149 * if the serialised data contains information about a field
150 * which does actually not exist in the class we know of
151 * @throws NoSuchMethodException
152 * if a class described in the serialised data cannot be created
153 * because it is not compatible with this code
154 * @throws ClassNotFoundException
155 * if a class described in the serialised data cannot be found
157 protected Object
send(Object data
) throws IOException
,
158 NoSuchFieldException
, NoSuchMethodException
, ClassNotFoundException
{
159 synchronized (lock
) {
160 String rep
= sendString(new Exporter().append(data
).toString(true));
162 return new Importer().read(rep
).getValue();
170 * Reserved for the server: flush the data to the client and retrieve its
173 * Also used internally for the client (only do something if there is
176 * Will only flush the data if there is contentToSend.
178 * @return the deserialised answer (which can actually be NULL)
180 * @throws IOException
181 * in case of I/O error
182 * @throws NoSuchFieldException
183 * if the serialised data contains information about a field
184 * which does actually not exist in the class we know of
185 * @throws NoSuchMethodException
186 * if a class described in the serialised data cannot be created
187 * because it is not compatible with this code
188 * @throws ClassNotFoundException
189 * if a class described in the serialised data cannot be found
190 * @throws java.lang.NullPointerException
191 * if the counter part has no data to send
193 protected Object
rec() throws IOException
, NoSuchFieldException
,
194 NoSuchMethodException
, ClassNotFoundException
,
195 java
.lang
.NullPointerException
{
196 String str
= flushString();
198 throw new NullPointerException("No more data available");
201 return new Importer().read(str
).getValue();
205 * Send the given string to the counter part (and, only for client, return
206 * the answer -- the server will always receive NULL).
209 * the data to send (we will add a line feed)
211 * @return the answer if this action is a client (without the added line
212 * feed), NULL if it is a server
214 * @throws IOException
215 * in case of I/O error
217 protected String
sendString(String line
) throws IOException
{
218 synchronized (lock
) {
227 contentToSend
= true;
228 return flushString();
233 * Reserved for the server (externally): flush the data to the client and
234 * retrieve its answer.
236 * Also used internally for the client (only do something if there is
239 * Will only flush the data if there is contentToSend.
241 * @return the answer (which can be NULL)
243 * @throws IOException
244 * in case of I/O error
246 protected String
flushString() throws IOException
{
247 synchronized (lock
) {
248 if (server
|| contentToSend
) {
251 contentToSend
= false;
254 String line
= in
.readLine();
255 if (server
&& line
!= null && line
.startsWith("VERSION ")) {
256 // "VERSION client-version" (VERSION 1.0.0)
257 Version clientVersion
= new Version(
258 line
.substring("VERSION ".length()));
259 this.clientVersion
= clientVersion
;
260 Version v
= negotiateVersion(clientVersion
);
264 sendString("VERSION " + v
.toString());
266 line
= in
.readLine();