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