Throw an exception when the node is not connected.
[jSite.git] / src / de / todesbaum / util / freenet / fcp2 / Client.java
1 /*
2  * todesbaum-lib -
3  * Copyright (C) 2006 David Roden
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 package de.todesbaum.util.freenet.fcp2;
21
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import de.todesbaum.util.io.StreamCopier.ProgressListener;
27
28 /**
29  * A Client executes {@link Command}s over a {@link Connection} to a
30  * {@link Node} and delivers resulting {@link Message}s.
31  *
32  * @author David Roden <droden@gmail.com>
33  * @version $Id$
34  */
35 public class Client implements ConnectionListener {
36
37         /** The connection this client operates on. */
38         private final Connection connection;
39
40         /** The identifiers the client filters messages for. */
41         private List<String> identifiers = new ArrayList<String>();
42
43         /** The queued messages. */
44         private final List<Message> messageQueue = new ArrayList<Message>();
45
46         /** Whether the client was disconnected. */
47         private boolean disconnected = false;
48
49         /** Whether to catch all messages from the connection. */
50         private boolean catchAll = false;
51
52         /**
53          * Creates a new client that operates on the specified connection.
54          *
55          * @param connection
56          *            The connection to operate on
57          */
58         public Client(Connection connection) {
59                 this.connection = connection;
60                 connection.addConnectionListener(this);
61         }
62
63         /**
64          * Creates a new client that operates on the specified connection and
65          * immediately executes the specified command.
66          *
67          * @param connection
68          *            The connection to operate on
69          * @param command
70          *            The command to execute
71          * @throws IOException
72          *             if an I/O error occurs
73          * @see #execute(Command)
74          */
75         public Client(Connection connection, Command command) throws IOException {
76                 this(connection);
77                 execute(command);
78         }
79
80         /**
81          * Returns whether this client catches all messages going over the
82          * connection.
83          *
84          * @return <code>true</code> if the client catches all messages,
85          *         <code>false</code> otherwise
86          */
87         public boolean isCatchAll() {
88                 return catchAll;
89         }
90
91         /**
92          * Sets whether this client catches all messages going over the connection.
93          *
94          * @param catchAll
95          *            <code>true</code> if the client should catch all messages,
96          *            <code>false</code> otherwise
97          */
98         public void setCatchAll(boolean catchAll) {
99                 this.catchAll = catchAll;
100         }
101
102         /**
103          * Executes the specified command. This will also clear the queue of
104          * messages, discarding all messages that resulted from the previous command
105          * and have not yet been read.
106          *
107          * @param command
108          *            The command to execute
109          * @throws IOException
110          *             if an I/O error occurs
111          * @see #execute(Command, boolean)
112          */
113         public void execute(Command command) throws IOException {
114                 execute(command, true);
115         }
116
117         /**
118          * Executes the specified command. This will also clear the queue of
119          * messages, discarding all messages that resulted from the previous
120          * command and have not yet been read.
121          *
122          * @param command
123          *            The command to execute
124          * @param progressListener
125          *            The progress listener for payload transfers
126          * @throws IOException
127          *             if an I/O error occurs
128          * @see #execute(Command, boolean)
129          */
130         public void execute(Command command, ProgressListener progressListener) throws IOException {
131                 execute(command, true, progressListener);
132         }
133
134         /**
135          * Executes the specified command and optionally clears the list of
136          * identifiers this clients listens to before starting the command.
137          *
138          * @param command
139          *            The command to execute
140          * @param removeExistingIdentifiers
141          *            If <code>true</code>, the list of identifiers that this
142          *            clients listens to is cleared
143          * @throws IOException
144          *             if an I/O error occurs
145          */
146         public void execute(Command command, boolean removeExistingIdentifiers) throws IOException {
147                 execute(command, removeExistingIdentifiers, null);
148         }
149
150         /**
151          * Executes the specified command and optionally clears the list of
152          * identifiers this clients listens to before starting the command.
153          *
154          * @param command
155          *            The command to execute
156          * @param removeExistingIdentifiers
157          *            If <code>true</code>, the list of identifiers that this
158          *            clients listens to is cleared
159          * @param progressListener
160          *            The progress listener for payload transfers
161          * @throws IOException
162          *             if an I/O error occurs
163          */
164         public void execute(Command command, boolean removeExistingIdentifiers, ProgressListener progressListener) throws IOException {
165                 synchronized (messageQueue) {
166                         messageQueue.clear();
167                         if (removeExistingIdentifiers) {
168                                 identifiers.clear();
169                         }
170                         identifiers.add(command.getIdentifier());
171                 }
172                 connection.execute(command, progressListener);
173         }
174
175         /**
176          * Returns the next message, waiting endlessly for it, if need be. If you
177          * are not sure whether a message will arrive, better use
178          * {@link #readMessage(long)} to only wait for a specific time.
179          *
180          * @return The next message that resulted from the execution of the last
181          *         command
182          * @see #readMessage(long)
183          * @see #execute(Command)
184          */
185         public Message readMessage() {
186                 return readMessage(0);
187         }
188
189         /**
190          * Returns the next message. If the message queue is currently empty, at
191          * least <code>maxWaitTime</code> milliseconds will be waited for a
192          * message to arrive.
193          *
194          * @param maxWaitTime
195          *            The minimum time to wait for a message, in milliseconds
196          * @return The message, or <code>null</code> if no message arrived in time
197          *         or the client is currently disconnected
198          * @see #isDisconnected()
199          * @see Object#wait(long)
200          */
201         public Message readMessage(long maxWaitTime) {
202                 synchronized (messageQueue) {
203                         if (disconnected) {
204                                 return null;
205                         }
206                         if (messageQueue.size() == 0) {
207                                 try {
208                                         messageQueue.wait(maxWaitTime);
209                                 } catch (InterruptedException ie1) {
210                                 }
211                         }
212                         if (messageQueue.size() > 0) {
213                                 return messageQueue.remove(0);
214                         }
215                 }
216                 return null;
217         }
218
219         /**
220          * Returns whether the client is currently disconnected.
221          *
222          * @return <code>true</code> if the client is disconnected,
223          *         <code>false</code> otherwise
224          */
225         public boolean isDisconnected() {
226                 synchronized (messageQueue) {
227                         return disconnected;
228                 }
229         }
230
231         /**
232          * {@inheritDoc}
233          */
234         public void messageReceived(Connection connection, Message message) {
235                 synchronized (messageQueue) {
236                         if (catchAll || (message.getIdentifier().length() == 0) || identifiers.contains(message.getIdentifier())) {
237                                 messageQueue.add(message);
238                                 messageQueue.notify();
239                         }
240                 }
241         }
242
243         /**
244          * {@inheritDoc}
245          */
246         public void connectionTerminated(Connection connection) {
247                 synchronized (messageQueue) {
248                         disconnected = true;
249                         messageQueue.notify();
250                 }
251         }
252
253 }