8f73b8d3a22cba89ee3cd13b808aa6f63e9b3744
[jSite2.git] / src / net / pterodactylus / jsite / core / RequestManager.java
1 /*
2  * jSite2 - RequestManager.java -
3  * Copyright © 2008 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 net.pterodactylus.jsite.core;
21
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.logging.Level;
29 import java.util.logging.Logger;
30
31 import net.pterodactylus.fcp.highlevel.GetRequestResult;
32 import net.pterodactylus.fcp.highlevel.HighLevelCallback;
33 import net.pterodactylus.fcp.highlevel.HighLevelCallbackListener;
34 import net.pterodactylus.fcp.highlevel.HighLevelClient;
35 import net.pterodactylus.fcp.highlevel.HighLevelException;
36 import net.pterodactylus.fcp.highlevel.HighLevelProgress;
37 import net.pterodactylus.fcp.highlevel.HighLevelProgressListener;
38 import net.pterodactylus.fcp.highlevel.PutDirRequestResult;
39 import net.pterodactylus.fcp.highlevel.PutRequestResult;
40 import net.pterodactylus.fcp.highlevel.RequestListResult;
41 import net.pterodactylus.fcp.highlevel.RequestResult;
42 import net.pterodactylus.jsite.core.Request.Type;
43 import net.pterodactylus.util.logging.Logging;
44
45 /**
46  * The request manager keeps track of all the request on all connected nodes.
47  * The request manager is added to the {@link NodeManager} as a
48  * {@link NodeListener} so that it can fire request-removed events in case a
49  * node is disconnected.
50  * 
51  * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
52  * @version $Id$
53  */
54 public class RequestManager implements NodeListener, HighLevelProgressListener {
55
56         /** Logger. */
57         private static final Logger logger = Logging.getLogger(RequestManager.class.getName());
58
59         /** Request listeners. */
60         private List<RequestListener> requestListeners = Collections.synchronizedList(new ArrayList<RequestListener>());
61
62         /** The node manager. */
63         private NodeManager nodeManager;
64
65         /** Request lists for all nodes. */
66         @SuppressWarnings("unused")
67         private Map<Node, Map<String, Request>> nodeRequests = Collections.synchronizedMap(new HashMap<Node, Map<String, Request>>());
68
69         //
70         // EVENT MANAGEMENT
71         //
72
73         /**
74          * Adds a request listener.
75          * 
76          * @param requestListener
77          *            The request listener to add
78          */
79         public void addRequestListener(RequestListener requestListener) {
80                 requestListeners.add(requestListener);
81         }
82
83         /**
84          * Removes a request listener.
85          * 
86          * @param requestListener
87          *            The request listener to remove
88          */
89         public void removeRequestListener(RequestListener requestListener) {
90                 requestListeners.remove(requestListener);
91         }
92
93         /**
94          * Notifies all listeners that a request was added.
95          * 
96          * @param request
97          *            The request that was added
98          */
99         private void fireRequestAdded(Request request) {
100                 for (RequestListener requestListener: requestListeners) {
101                         requestListener.requestAdded(request);
102                 }
103         }
104
105         /**
106          * Notifies all listeners that a request progressed.
107          * 
108          * @param request
109          *            The request
110          */
111         private void fireRequestProgressed(Request request) {
112                 for (RequestListener requestListener: requestListeners) {
113                         requestListener.requestProgressed(request);
114                 }
115         }
116
117         /**
118          * Notifies all listeners that a request was removed.
119          * 
120          * @param request
121          *            The request that was removed
122          */
123         private void fireRequestRemoved(Request request) {
124                 for (RequestListener requestListener: requestListeners) {
125                         requestListener.requestRemoved(request);
126                 }
127         }
128
129         /**
130          * Notifies all listeners that a request has finished.
131          * 
132          * @see RequestListener#requestFinished(Request)
133          * @param request
134          *            The request that has finished
135          */
136         private void fireRequestFinished(Request request) {
137                 for (RequestListener requestListener: requestListeners) {
138                         requestListener.requestFinished(request);
139                 }
140         }
141
142         /**
143          * Notifies all listeners that a request has generated a URI.
144          * 
145          * @see RequestListener#requestGeneratedURI(Request, String)
146          * @param request
147          *            The request that has generated a URI
148          * @param uri
149          *            The generated URI
150          */
151         private void fireRequestGeneratedURI(Request request, String uri) {
152                 for (RequestListener requestListener: requestListeners) {
153                         requestListener.requestGeneratedURI(request, uri);
154                 }
155         }
156
157         //
158         // ACCESSORS
159         //
160
161         /**
162          * Sets the node manager to use.
163          * 
164          * @param nodeManager
165          *            The node manager
166          */
167         public void setNodeManager(NodeManager nodeManager) {
168                 this.nodeManager = nodeManager;
169         }
170
171         //
172         // ACTIONS
173         //
174
175         //
176         // PRIVATE ACTIONS
177         //
178
179         /**
180          * Requests a list of all running requests from a node. This method will
181          * block until the request has been sent!
182          * 
183          * @param node
184          *            The node to get all requests for
185          * @throws IOException
186          *             if an I/O error occurs while communicating with the node
187          * @throws JSiteException
188          *             if there is a problem with the node
189          */
190         private void getRequests(final Node node) throws IOException, JSiteException {
191                 HighLevelClient highLevelClient = nodeManager.getHighLevelClient(node);
192                 if (highLevelClient == null) {
193                         logger.log(Level.WARNING, "no client for node: " + node);
194                         return;
195                 }
196                 final Map<String, Request> identifierRequests = Collections.synchronizedMap(new HashMap<String, Request>());
197                 nodeRequests.put(node, identifierRequests);
198                 HighLevelCallback<RequestListResult> requestListCallback;
199                 try {
200                         requestListCallback = highLevelClient.getRequests();
201                         requestListCallback.addHighLevelCallbackListener(new HighLevelCallbackListener<RequestListResult>() {
202
203                                 @SuppressWarnings("synthetic-access")
204                                 public void gotResult(HighLevelCallback<RequestListResult> highLevelCallback) {
205                                         RequestListResult requestListResult;
206                                         try {
207                                                 requestListResult = highLevelCallback.getResult();
208                                         } catch (InterruptedException e) {
209                                                 logger.log(Level.SEVERE, "getResult() blocked and was interrupted");
210                                                 return;
211                                         }
212                                         for (RequestResult requestResult: requestListResult) {
213                                                 String identifier = requestResult.getIdentifier();
214                                                 logger.log(Level.FINER, "got identifier: " + identifier);
215                                                 Request request = identifierRequests.get(identifier);
216                                                 if (request == null) {
217                                                         request = new Request(node, identifier);
218                                                 }
219                                                 if (request.getType() == null) {
220                                                         if (requestResult instanceof GetRequestResult) {
221                                                                 request.setType(Type.get);
222                                                         } else if (requestResult instanceof PutRequestResult) {
223                                                                 request.setType(Type.put);
224                                                         } else if (requestResult instanceof PutDirRequestResult) {
225                                                                 request.setType(Type.putDir);
226                                                         }
227                                                 }
228                                                 if (identifierRequests.containsKey(identifier)) {
229                                                         continue;
230                                                 }
231                                                 identifierRequests.put(requestResult.getIdentifier(), request);
232                                                 fireRequestAdded(request);
233                                         }
234                                 }
235                         });
236                 } catch (HighLevelException hle1) {
237                         throw new BackendException(hle1);
238                 }
239         }
240
241         //
242         // INTERFACE NodeListener
243         //
244
245         /**
246          * {@inheritDoc}
247          */
248         public void nodeAdded(Node node) {
249                 HighLevelClient highLevelClient = nodeManager.getHighLevelClient(node);
250                 if (highLevelClient == null) {
251                         logger.warning("got nodeAdded but no high-level client: " + node);
252                         return;
253                 }
254                 highLevelClient.addHighLevelProgressListener(this);
255         }
256
257         /**
258          * {@inheritDoc}
259          */
260         public void nodeRemoved(Node node) {
261                 HighLevelClient highLevelClient = nodeManager.getHighLevelClient(node);
262                 if (highLevelClient == null) {
263                         logger.warning("got nodeRemoved but no high-level client: " + node);
264                         return;
265                 }
266                 highLevelClient.removeHighLevelProgressListener(this);
267         }
268
269         /**
270          * {@inheritDoc}
271          */
272         public void nodeConnected(Node node) {
273                 HighLevelClient highLevelClient = nodeManager.getHighLevelClient(node);
274                 if (highLevelClient == null) {
275                         logger.log(Level.WARNING, "got no high-level client for node " + node);
276                         return;
277                 }
278                 try {
279                         highLevelClient.setWatchGlobal(true);
280                         getRequests(node);
281                 } catch (HighLevelException hle1) {
282                         logger.log(Level.WARNING, "error in backend", hle1);
283                 } catch (JSiteException jse1) {
284                         logger.log(Level.WARNING, "error in backend", jse1);
285                 } catch (IOException e) {
286                         /* ignore exception, disconnects are handled elsewhere. */
287                 }
288         }
289
290         /**
291          * {@inheritDoc}
292          */
293         public void nodeConnectionFailed(Node node, Throwable cause) {
294                 /* we don't care about this. */
295         }
296
297         /**
298          * {@inheritDoc}
299          */
300         public void nodeDisconnected(Node node, Throwable throwable) {
301                 Map<String, Request> identifierRequests = nodeRequests.get(node);
302                 if (identifierRequests == null) {
303                         logger.warning("got node without request map: " + node);
304                         return;
305                 }
306                 for (Request request: identifierRequests.values()) {
307                         fireRequestRemoved(request);
308                 }
309                 identifierRequests.clear();
310         }
311
312         //
313         // INTERFACE HighLevelProgressListener
314         //
315
316         /**
317          * @see net.pterodactylus.fcp.highlevel.HighLevelProgressListener#progressReceived(HighLevelClient,
318          *      String, HighLevelProgress)
319          */
320         public void progressReceived(HighLevelClient highLevelClient, String identifier, HighLevelProgress highLevelProgress) {
321                 Node node = nodeManager.getNode(highLevelClient);
322                 if (node == null) {
323                         logger.warning("got high-level client without node: " + highLevelClient);
324                         return;
325                 }
326                 Map<String, Request> identifierRequests = nodeRequests.get(node);
327                 if (identifierRequests == null) {
328                         logger.warning("got node without request map: " + node);
329                         identifierRequests = Collections.synchronizedMap(new HashMap<String, Request>());
330                         nodeRequests.put(node, identifierRequests);
331                 }
332                 Request request = identifierRequests.get(identifier);
333                 if (request == null) {
334                         logger.warning("got progress for unknown request: " + identifier);
335                         request = new Request(node, identifier);
336                         identifierRequests.put(identifier, request);
337                         fireRequestAdded(request);
338                 }
339                 if (highLevelProgress.isFinished()) {
340                         request.setFinished(true);
341                         request.setSuccessful(!highLevelProgress.isFailed());
342                         fireRequestFinished(request);
343                 } else if (highLevelProgress.isFetchable()) {
344                         /* TODO - ignore? */
345                 } else if (highLevelProgress.getURI() != null) {
346                         request.setURI(highLevelProgress.getURI());
347                         fireRequestGeneratedURI(request, highLevelProgress.getURI());
348                 } else {
349                         request.setTotalBlocks(highLevelProgress.getTotalBlocks());
350                         request.setRequiredBlocks(highLevelProgress.getRequiredBlocks());
351                         request.setSuccessfulBlocks(highLevelProgress.getSuccessfulBlocks());
352                         request.setFailedBlocks(highLevelProgress.getFailedBlocks());
353                         request.setFatallyFailedBlocks(highLevelProgress.getFatallyFailedBlocks());
354                         request.setTotalFinalized(highLevelProgress.isTotalFinalized());
355                         fireRequestProgressed(request);
356                 }
357         }
358
359 }