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