jSite: First commit : verion 4.0 (written by Bombe)
[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 /**
27  * A Client executes {@link Command}s over a {@link Connection} to a
28  * {@link Node} and delivers resulting {@link Message}s.
29  * 
30  * @author David Roden <droden@gmail.com>
31  * @version $Id: Client.java 413 2006-03-29 12:22:31Z bombe $
32  */
33 public class Client implements ConnectionListener {
34
35         /** The connection this client operates on. */
36         private final Connection connection;
37
38         /** The identifiers the client filters messages for. */
39         private List<String> identifiers = new ArrayList<String>();
40
41         /** The queued messages. */
42         private final List<Message> messageQueue = new ArrayList<Message>();
43
44         /** Whether the client was disconnected. */
45         private boolean disconnected = false;
46
47         /** Whether to catch all messages from the connection. */
48         private boolean catchAll = false;
49
50         /**
51          * Creates a new client that operates on the specified connection.
52          * 
53          * @param connection
54          *            The connection to operate on
55          */
56         public Client(Connection connection) {
57                 this.connection = connection;
58                 connection.addConnectionListener(this);
59         }
60
61         /**
62          * Creates a new client that operates on the specified connection and
63          * immediately executes the specified command.
64          * 
65          * @param connection
66          *            The connection to operate on
67          * @param command
68          *            The command to execute
69          * @throws IOException
70          *             if an I/O error occurs
71          * @see #execute(Command)
72          */
73         public Client(Connection connection, Command command) throws IOException {
74                 this(connection);
75                 execute(command);
76         }
77
78         /**
79          * Returns whether this client catches all messages going over the
80          * connection.
81          * 
82          * @return <code>true</code> if the client catches all messages,
83          *         <code>false</code> otherwise
84          */
85         public boolean isCatchAll() {
86                 return catchAll;
87         }
88
89         /**
90          * Sets whether this client catches all messages going over the connection.
91          * 
92          * @param catchAll
93          *            <code>true</code> if the client should catch all messages,
94          *            <code>false</code> otherwise
95          */
96         public void setCatchAll(boolean catchAll) {
97                 this.catchAll = catchAll;
98         }
99
100         /**
101          * Executes the specified command. This will also clear the queue of
102          * messages, discarding all messages that resulted from the previous command
103          * and have not yet been read.
104          * 
105          * @param command
106          *            The command to execute
107          * @throws IOException
108          *             if an I/O error occurs
109          * @see #execute(Command, boolean)
110          */
111         public void execute(Command command) throws IOException {
112                 execute(command, true);
113         }
114
115         /**
116          * Executes the specified command and optionally clears the list of
117          * identifiers this clients listens to before starting the command.
118          * 
119          * @param command
120          *            The command to execute
121          * @param removeExistingIdentifiers
122          *            If <code>true</code>, the list of identifiers that this
123          *            clients listens to is cleared
124          * @throws IOException
125          *             if an I/O error occurs
126          */
127         public void execute(Command command, boolean removeExistingIdentifiers) throws IOException {
128                 synchronized (messageQueue) {
129                         messageQueue.clear();
130                         if (removeExistingIdentifiers) {
131                                 identifiers.clear();
132                         }
133                         identifiers.add(command.getIdentifier());
134                 }
135                 connection.execute(command);
136         }
137
138         /**
139          * Returns the next message, waiting endlessly for it, if need be. If you
140          * are not sure whether a message will arrive, better use
141          * {@link #readMessage(long)} to only wait for a specific time.
142          * 
143          * @return The next message that resulted from the execution of the last
144          *         command
145          * @see #readMessage(long)
146          * @see #execute(Command)
147          */
148         public Message readMessage() {
149                 return readMessage(0);
150         }
151
152         /**
153          * Returns the next message. If the message queue is currently empty, at
154          * least <code>maxWaitTime</code> milliseconds will be waited for a
155          * message to arrive.
156          * 
157          * @param maxWaitTime
158          *            The minimum time to wait for a message, in milliseconds
159          * @return The message, or <code>null</code> if no message arrived in time
160          *         or the client is currently disconnected
161          * @see #isDisconnected()
162          * @see Object#wait(long)
163          */
164         public Message readMessage(long maxWaitTime) {
165                 synchronized (messageQueue) {
166                         if (disconnected) {
167                                 return null;
168                         }
169                         if (messageQueue.size() == 0) {
170                                 try {
171                                         messageQueue.wait(maxWaitTime);
172                                 } catch (InterruptedException ie1) {
173                                 }
174                         }
175                         if (messageQueue.size() > 0) {
176                                 return messageQueue.remove(0);
177                         }
178                 }
179                 return null;
180         }
181
182         /**
183          * Returns whether the client is currently disconnected.
184          * 
185          * @return <code>true</code> if the client is disconnected,
186          *         <code>false</code> otherwise
187          */
188         public boolean isDisconnected() {
189                 synchronized (messageQueue) {
190                         return disconnected;
191                 }
192         }
193
194         /**
195          * {@inheritDoc}
196          */
197         public void messageReceived(Connection connection, Message message) {
198                 synchronized (messageQueue) {
199                         if (catchAll || (message.getIdentifier().length() == 0) || identifiers.contains(message.getIdentifier())) {
200                                 messageQueue.add(message);
201                                 messageQueue.notify();
202                         }
203                 }
204         }
205
206         /**
207          * {@inheritDoc}
208          */
209         public void connectionTerminated(Connection connection) {
210                 synchronized (messageQueue) {
211                         disconnected = true;
212                         messageQueue.notify();
213                 }
214         }
215
216 }