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 javax
.net
.ssl
.SSLException
;
11 import be
.nikiroo
.utils
.Version
;
12 import be
.nikiroo
.utils
.serial
.Exporter
;
13 import be
.nikiroo
.utils
.serial
.Importer
;
16 * Base class used for the client/server basic handling.
18 * It represents a single action: a client is expected to only execute one
19 * action, while a server is expected to execute one action for each client
24 abstract class ConnectAction
{
26 private boolean server
;
27 private Version version
;
28 private Version clientVersion
;
30 private Object lock
= new Object();
31 private BufferedReader in
;
32 private OutputStreamWriter out
;
33 private boolean contentToSend
;
36 * Method that will be called when an action is performed on either the
37 * client or server this {@link ConnectAction} represent.
40 * the counter part version
43 * in case of I/O error
45 abstract protected void action(Version version
) throws Exception
;
48 * Method called when we negotiate the version with the client.
50 * Thus, it is only called on the server.
52 * Will return the actual server version by default.
54 * @param clientVersion
57 * @return the version to send to the client
59 abstract protected Version
negotiateVersion(Version clientVersion
);
62 * Handler called when an unexpected error occurs in the code.
65 * the exception that occurred
67 abstract protected void onError(Exception e
);
70 * Create a new {@link ConnectAction}.
73 * the socket to bind to
75 * TRUE for a server action, FALSE for a client action (will
78 * the version of this client-or-server
80 protected ConnectAction(Socket s
, boolean server
, Version version
) {
84 if (version
== null) {
85 this.version
= new Version();
87 this.version
= version
;
90 clientVersion
= new Version();
94 * The version of this client-or-server.
98 public Version
getVersion() {
103 * Actually start the process (this is synchronous).
105 public void connect() {
107 in
= new BufferedReader(
108 new InputStreamReader(s
.getInputStream(), "UTF-8"));
110 out
= new OutputStreamWriter(s
.getOutputStream(), "UTF-8");
113 String line
= in
.readLine();
114 if (line
!= null && line
.startsWith("VERSION ")) {
115 // "VERSION client-version" (VERSION 1.0.0)
116 Version clientVersion
= new Version(
117 line
.substring("VERSION ".length()));
118 this.clientVersion
= clientVersion
;
119 Version v
= negotiateVersion(clientVersion
);
124 sendString("VERSION " + v
.toString());
127 action(clientVersion
);
129 String v
= sendString("VERSION " + version
.toString());
130 if (v
!= null && v
.startsWith("VERSION ")) {
131 v
= v
.substring("VERSION ".length());
134 action(new Version(v
));
144 } catch (Exception e
) {
145 if (e
instanceof SSLException
) {
147 for (String cipher
: Server
.getAnonCiphers()) {
148 if (!ciphers
.isEmpty()) {
154 e
= new SSLException(
155 "SSL error (available SSL ciphers: " + ciphers
+ ")",
163 } catch (Exception e
) {
170 * Serialise and send the given object to the counter part (and, only for
171 * client, return the deserialised answer -- the server will always receive
177 * @return the answer (which can be NULL) if this action is a client, always
178 * NULL if it is a server
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
191 protected Object
sendObject(Object data
)
192 throws IOException
, NoSuchFieldException
, NoSuchMethodException
,
193 ClassNotFoundException
{
194 synchronized (lock
) {
195 String rep
= sendString(
196 new Exporter().append(data
).toString(true, true));
198 return new Importer().read(rep
).getValue();
206 * Reserved for the server: flush the data to the client and retrieve its
209 * Also used internally for the client (only do something if there is
212 * Will only flush the data if there is contentToSend.
214 * @return the deserialised answer (which can actually be NULL)
216 * @throws IOException
217 * in case of I/O error
218 * @throws NoSuchFieldException
219 * if the serialised data contains information about a field
220 * which does actually not exist in the class we know of
221 * @throws NoSuchMethodException
222 * if a class described in the serialised data cannot be created
223 * because it is not compatible with this code
224 * @throws ClassNotFoundException
225 * if a class described in the serialised data cannot be found
226 * @throws java.lang.NullPointerException
227 * if the counter part has no data to send
229 protected Object
recObject()
230 throws IOException
, NoSuchFieldException
, NoSuchMethodException
,
231 ClassNotFoundException
, java
.lang
.NullPointerException
{
232 String str
= recString();
234 throw new NullPointerException("No more data available");
237 return new Importer().read(str
).getValue();
241 * Send the given string to the counter part (and, only for client, return
242 * the answer -- the server will always receive NULL).
245 * the data to send (we will add a line feed)
247 * @return the answer if this action is a client (without the added line
248 * feed), NULL if it is a server
250 * @throws IOException
251 * in case of I/O error
253 protected String
sendString(String line
) throws IOException
{
254 synchronized (lock
) {
263 contentToSend
= true;
269 * Reserved for the server (externally): flush the data to the client and
270 * retrieve its answer.
272 * Also used internally for the client (only do something if there is
275 * Will only flush the data if there is contentToSend.
277 * @return the answer (which can be NULL)
279 * @throws IOException
280 * in case of I/O error
282 protected String
recString() throws IOException
{
283 synchronized (lock
) {
284 if (server
|| contentToSend
) {
287 contentToSend
= false;
290 return in
.readLine();