1 package be
.nikiroo
.jvcard
.remote
;
3 import java
.io
.BufferedReader
;
4 import java
.io
.Closeable
;
5 import java
.io
.IOException
;
6 import java
.io
.InputStreamReader
;
7 import java
.io
.PrintWriter
;
8 import java
.net
.InetAddress
;
9 import java
.net
.Socket
;
10 import java
.util
.Arrays
;
11 import java
.util
.LinkedList
;
12 import java
.util
.List
;
15 * A client or server connection, that will allow you to connect to, send and
16 * receive data to/from a jVCard remote server.
22 public class SimpleSocket
{
24 * An {@link Appendable} that can be used to send data over a
25 * {@link SimpleSocket}. You must close it to send the end of block element.
30 public class BlockAppendable
implements Appendable
, Closeable
{
31 private SimpleSocket ss
;
34 * Create a new {@link BlockAppendable} for the given
35 * {@link SimpleSocket}.
38 * the {@link SimpleSocket}
40 public BlockAppendable(SimpleSocket ss
) {
45 public Appendable
append(CharSequence csq
) throws IOException
{
51 public Appendable
append(char c
) throws IOException
{
57 public Appendable
append(CharSequence csq
, int start
, int end
)
59 ss
.send(csq
.subSequence(start
, end
));
64 public void close() throws IOException
{
71 * The current version of the network protocol.
73 static public final int CURRENT_VERSION
= 1;
76 * The end of block marker.
78 * An end of block marker needs to be on a line on itself to be valid, and
79 * will denote the end of a block of data.
81 static private String EOB
= ".";
84 private PrintWriter out
;
85 private BufferedReader in
;
86 private int version
; // version of the OTHER end, not this one (this one is
87 // CURRENT_VERSION obviously)
89 private String label
; // can be used for debugging
92 * Create a new {@link SimpleSocket} with the given {@link Socket}.
97 public SimpleSocket(Socket s
, String label
) {
103 * Return the label of this {@link SimpleSocket}. This is mainly used for
104 * debugging purposes or user display if any. It is optional.
108 public String
getLabel() {
115 * Open the {@link SimpleSocket} for reading/writing and negotiates the
118 * Note that you <b>MUST</b> call {@link SimpleSocket#close()} when you are
119 * done to release the acquired resources.
122 * TRUE for clients, FALSE for servers (server speaks first)
124 * @throws IOException
125 * in case of IO error
127 public void open(boolean client
) throws IOException
{
128 out
= new PrintWriter(s
.getOutputStream(), false);
129 in
= new BufferedReader(new InputStreamReader(s
.getInputStream()));
132 version
= new CommandInstance(receiveLine(), -1).getVersion();
133 sendLine(new CommandInstance(Command
.VERSION
, CURRENT_VERSION
)
136 send(new CommandInstance(Command
.VERSION
, CURRENT_VERSION
)
139 send("[Some help info here]");
140 send("you need to reply with your VERSION + end of block");
141 send("please send HELP in a full block or help");
143 version
= new CommandInstance(receiveLine(), -1).getVersion();
148 * Close the connection and release acquired resources.
150 * @return TRUE if everything was closed properly, FALSE if the connection
151 * was broken (in all cases, resources are released)
153 public boolean close() {
154 boolean broken
= false;
158 broken
= out
.checkError();
159 } catch (IOException e
) {
165 } catch (IOException e
) {
170 } catch (IOException ee
) {
183 * Sends lines to the remote server. Do <b>NOT</b> sends the end-of-block
189 * @throws IOException
190 * in case of IO error
192 protected void send(CharSequence data
) throws IOException
{
199 if (out
.checkError())
200 throw new IOException();
204 * Sends an end-of-block marker to the remote server.
206 * @throws IOException
207 * in case of IO error
209 public void sendBlock() throws IOException
{
210 sendBlock((List
<String
>) null);
214 * Sends commands to the remote server, then sends an end-of-block marker.
219 * @throws IOException
220 * in case of IO error
222 public void sendLine(String data
) throws IOException
{
223 sendBlock(Arrays
.asList(new String
[] { data
}));
227 * Sends commands to the remote server, then sends an end-of-block marker.
232 * @throws IOException
233 * in case of IO error
235 public void sendBlock(List
<String
> data
) throws IOException
{
237 for (String dataLine
: data
) {
246 * Sends commands to the remote server, then sends an end-of-block marker.
249 * the {@link Command} to send
251 * @throws IOException
252 * in case of IO error
254 public void sendCommand(Command command
) throws IOException
{
255 sendCommand(command
, null);
259 * Sends commands to the remote server, then sends an end-of-block marker.
265 * the parameter for this command if any
267 * @throws IOException
268 * in case of IO error
270 public void sendCommand(Command command
, String param
) throws IOException
{
271 sendLine(new CommandInstance(command
, param
, CURRENT_VERSION
)
276 * Create a new {@link Appendable} that can be used to send data on this
277 * {@link SimpleSocket}. When you are done, just call
278 * {@link BlockAppendable#close()}.
280 * @return the {@link Appendable}
282 public BlockAppendable
createBlockAppendable() {
283 return new BlockAppendable(this);
287 * Read a line from the remote server.
289 * Do <b>NOT</b> read until the end-of-block marker, and can return said
290 * block without conversion.
292 * @return the read line
294 * @throws IOException
295 * in case of IO error
297 protected String
receive() throws IOException
{
298 String line
= in
.readLine();
303 * Read lines from the remote server until the end-of-block ("\0\n") marker
306 * @return the read lines without the end marker, or NULL if nothing more to
309 * @throws IOException
310 * in case of IO error
312 public List
<String
> receiveBlock() throws IOException
{
313 List
<String
> result
= new LinkedList
<String
>();
315 String line
= receive();
316 for (; line
!= null && !line
.equals(EOB
); line
= receive()) {
327 * Read a line from the remote server then read until the next end-of-block
330 * @return the parsed line, or NULL if nothing more to read
332 * @throws IOException
333 * in case of IO error
335 public String
receiveLine() throws IOException
{
336 List
<String
> lines
= receiveBlock();
338 if (lines
.size() > 0)
345 * Read a line from the remote server and convert it to a
346 * {@link CommandInstance}, then read until the next end-of-block marker.
348 * @return the parsed {@link CommandInstance}
350 * @throws IOException
351 * in case of IO error
353 public CommandInstance
receiveCommand() throws IOException
{
354 String line
= receive();
355 CommandInstance cmd
= new CommandInstance(line
, version
);
361 public String
toString() {
362 String source
= "[not connected]";
363 InetAddress iadr
= s
.getInetAddress();
365 source
= iadr
.getHostName();
367 return getLabel() + " (" + source
+ ")";