import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.logging.Level;
import java.util.logging.Logger;
import net.pterodactylus.fcp.AddPeer;
import net.pterodactylus.fcp.GetFailed;
import net.pterodactylus.fcp.IdentifierCollision;
import net.pterodactylus.fcp.ListPeers;
+import net.pterodactylus.fcp.ListPersistentRequests;
import net.pterodactylus.fcp.NodeData;
import net.pterodactylus.fcp.NodeHello;
import net.pterodactylus.fcp.NodeRef;
import net.pterodactylus.fcp.URIGenerated;
import net.pterodactylus.fcp.UnknownNodeIdentifier;
import net.pterodactylus.fcp.UnknownPeerNoteType;
+import net.pterodactylus.fcp.WatchGlobal;
/**
* A high-level client that allows simple yet full-featured access to a Freenet
* node.
- *
+ *
* @author David ‘Bombe’ Roden <bombe@freenetproject.org>
* @version $Id$
*/
/** The listener for the connection. */
private HighLevelClientFcpListener highLevelClientFcpListener = new HighLevelClientFcpListener();
+ /** The listeners for progress events. */
+ private List<HighLevelProgressListener> highLevelProgressListeners = Collections.synchronizedList(new ArrayList<HighLevelProgressListener>());
+
/** The callback for {@link #connect()}. */
private HighLevelCallback<ConnectResult> connectCallback;
/** Mapping from request identifiers to download callbacks. */
private Map<String, HighLevelProgressCallback<DownloadResult>> downloadCallbacks = Collections.synchronizedMap(new HashMap<String, HighLevelProgressCallback<DownloadResult>>());
+ /** The callback for {@link #getRequests()}. */
+ private HighLevelCallback<RequestListResult> requestListCallback;
+
/**
* Creates a new high-level client that connects to a node on
* <code>localhost</code>.
- *
+ *
* @param clientName
* The name of the client
* @throws UnknownHostException
/**
* Creates a new high-level client that connects to a node on the given
* host.
- *
+ *
* @param clientName
* The name of the client
* @param host
/**
* Creates a new high-level client that connects to a node on the given
* host.
- *
+ *
* @param clientName
* The name of the client
* @param host
/**
* Creates a new high-level client that connects to a node at the given
* address.
- *
+ *
* @param clientName
* The name of the client
* @param address
/**
* Adds the given high-level client listener to list of listeners.
- *
+ *
* @param highLevelClientListener
* The listener to add
*/
/**
* Removes the given high-level client listener from the list of listeners.
- *
+ *
* @param highLevelClientListener
* The listener to remove
*/
/**
* Notifies all listeners that a client has disconnected.
+ *
+ * @param throwable
+ * The exception that caused the disconnect, or <code>null</code>
+ * if there was no exception
*/
- private void fireClientDisconnected() {
+ private void fireClientDisconnected(Throwable throwable) {
for (HighLevelClientListener highLevelClientListener: highLevelClientListeners) {
- highLevelClientListener.clientDisconnected(this);
+ highLevelClientListener.clientDisconnected(this, throwable);
+ }
+ }
+
+ /**
+ * Adds a high-level progress listener.
+ *
+ * @param highLevelProgressListener
+ * The high-level progress listener to add
+ */
+ public void addHighLevelProgressListener(HighLevelProgressListener highLevelProgressListener) {
+ highLevelProgressListeners.add(highLevelProgressListener);
+ }
+
+ /**
+ * Removes a high-level progress listener.
+ *
+ * @param highLevelProgressListener
+ * The high-level progress listener to remove
+ */
+ public void removeHighLevelProgressListener(HighLevelProgressListener highLevelProgressListener) {
+ highLevelProgressListeners.remove(highLevelProgressListener);
+ }
+
+ /**
+ * Notifies all listeners that the request with the given identifier made
+ * some progress.
+ *
+ * @param identifier
+ * The identifier of the request
+ * @param highLevelProgress
+ * The progress of the request
+ */
+ private void fireProgressReceived(String identifier, HighLevelProgress highLevelProgress) {
+ for (HighLevelProgressListener highLevelProgressListener: highLevelProgressListeners) {
+ highLevelProgressListener.progressReceived(identifier, highLevelProgress);
}
}
* Returns the FCP connection that backs this high-level client. This method
* should be used with care as fiddling around with the FCP connection can
* easily break the high-level client if you don’t know what you’re doing!
- *
+ *
* @return The FCP connection of this client
*/
public FcpConnection getFcpConnection() {
/**
* Connects the client.
- *
+ *
* @return A callback with a connection result
* @throws IOException
* if an I/O error occurs communicating with the node
public HighLevelCallback<ConnectResult> connect() throws IOException {
fcpConnection = new FcpConnection(address, port);
fcpConnection.addFcpListener(highLevelClientFcpListener);
+ fcpConnection.connect();
ClientHello clientHello = new ClientHello(clientName);
connectCallback = new HighLevelCallback<ConnectResult>(new ConnectResult());
fcpConnection.sendMessage(clientHello);
* Disconnects the client from the node.
*/
public void disconnect() {
- fcpConnection.close();
- fireClientDisconnected();
+ disconnect(null);
}
/**
* Generates a new SSK keypair.
- *
+ *
* @return A callback with the keypair
* @throws IOException
* if an I/O error occurs communicating with the node
}
/**
+ * Sets whether to watch the global queue.
+ *
+ * @param enabled
+ * <code>true</code> to watch the global queue in addition to
+ * the client-local queue, <code>false</code> to only watch the
+ * client-local queue
+ * @throws IOException
+ * if an I/O error occurs communicating with the node
+ */
+ public void setWatchGlobal(boolean enabled) throws IOException {
+ WatchGlobal watchGlobal = new WatchGlobal(enabled);
+ fcpConnection.sendMessage(watchGlobal);
+ }
+
+ /**
* Gets a list of all peers from the node.
- *
+ *
* @return A callback with the peer list
* @throws IOException
* if an I/O error occurs with the node
/**
* Adds the peer whose noderef is stored in the given file.
- *
+ *
* @param nodeRefFile
* The name of the file the peer’s noderef is stored in
* @return A peer callback
/**
* Adds the peer whose noderef is stored in the given file.
- *
+ *
* @param nodeRefURL
* The URL where the peer’s noderef is stored
* @return A peer callback
/**
* Adds the peer whose noderef is stored in the given file.
- *
+ *
* @param nodeRef
* The peer’s noderef
* @return A peer callback
* Checks whether direct disk access for the given directory is possible.
* You have to perform this check before you can upload or download anything
* from or the disk directly!
- *
+ *
* @param directory
* The directory to check
* @param wantRead
* Starts a download. Files can either be download to disk or streamed from
* the node. When downloading to disk you have to perform a direct disk
* access check for the directory you want to put the downloaded file in!
- *
+ *
* @see #checkDirectDiskAccess(String, boolean, boolean)
* @param uri
* The URI to get
return downloadCallback;
}
+ /**
+ * Requests a list of all running requests from the node.
+ *
+ * @return The request list result
+ * @throws IOException
+ * if an I/O errors communicating with the node
+ */
+ public HighLevelCallback<RequestListResult> getRequests() throws IOException {
+ String identifier = generateIdentifier("list-persistent-requests");
+ ListPersistentRequests listPersistentRequests = new ListPersistentRequests();
+ synchronized (syncObject) {
+ if (requestListCallback != null) {
+ logger.log(Level.SEVERE, "getRequests() called with request still running!");
+ }
+ requestListCallback = new HighLevelCallback<RequestListResult>(new RequestListResult(identifier));
+ }
+ fcpConnection.sendMessage(listPersistentRequests);
+ return requestListCallback;
+ }
+
//
// PRIVATE METHODS
//
/**
* Generates an identifier for the given function.
- *
+ *
* @param function
* The name of the function
* @return An identifier
}
/**
+ * Disconnects the client from the node, handing the given Throwable to
+ * {@link #fireClientDisconnected(Throwable)}.
+ *
+ * @param throwable
+ * The exception that caused the disconnect, or <code>null</code>
+ * if there was no exception
+ */
+ private void disconnect(Throwable throwable) {
+ if (fcpConnection != null) {
+ fcpConnection.close();
+ }
+ fireClientDisconnected(throwable);
+ }
+
+ /**
* FCP listener for {@link HighLevelClient}.
- *
+ *
* @author David ‘Bombe’ Roden <bombe@freenetproject.org>
* @version $Id$
*/
/**
* Searches all callback collections for a callback with the given
* identifier and cancels it.
- *
+ *
* @param identifier
* The identifier to search for, or <code>null</code> to
* cancel all pending requests
connectCallback.setDone();
connectCallback = null;
}
+ if (requestListCallback != null) {
+ requestListCallback.getIntermediaryResult().setFailed(true);
+ requestListCallback.setDone();
+ requestListCallback = null;
+ }
}
if (identifier == null) {
/* key generation callbacks */
/**
* Reads the given file and returns the first line of the file.
- *
+ *
* @param readFilename
* The name of the file to read
* @return The content of the file
/**
* Writes the given content to the given file.
- *
+ *
* @param directDiskAccessResult
* The DDA result
* @param writeFilename
/**
* Cleans up any files that written for the given result.
- *
+ *
* @param directDiskAccessResult
* The direct disk access result
*/
//
/**
- * @see net.pterodactylus.fcp.FcpListener#connectionClosed(net.pterodactylus.fcp.FcpConnection)
+ * @see net.pterodactylus.fcp.FcpListener#connectionClosed(net.pterodactylus.fcp.FcpConnection,
+ * Throwable)
*/
@SuppressWarnings("synthetic-access")
- public void connectionClosed(FcpConnection fcpConnection) {
+ public void connectionClosed(FcpConnection fcpConnection, Throwable throwable) {
if (fcpConnection != HighLevelClient.this.fcpConnection) {
return;
}
cancelIdentifier(null);
- disconnect();
+ disconnect(throwable);
}
/**
* @see net.pterodactylus.fcp.FcpListener#receivedEndListPersistentRequests(net.pterodactylus.fcp.FcpConnection,
* net.pterodactylus.fcp.EndListPersistentRequests)
*/
+ @SuppressWarnings("synthetic-access")
public void receivedEndListPersistentRequests(FcpConnection fcpConnection, EndListPersistentRequests endListPersistentRequests) {
- /* TODO */
+ if (fcpConnection != HighLevelClient.this.fcpConnection) {
+ return;
+ }
+ synchronized (syncObject) {
+ if (HighLevelClient.this.requestListCallback == null) {
+ logger.log(Level.WARNING, "got EndListPersistentRequests without running request!");
+ return;
+ }
+ requestListCallback.setDone();
+ requestListCallback = null;
+ }
}
/**
if (fcpConnection != HighLevelClient.this.fcpConnection) {
return;
}
+ synchronized (syncObject) {
+ if (requestListCallback != null) {
+ RequestListResult requestListResult = requestListCallback.getIntermediaryResult();
+ requestListResult.addRequestResult(new GetRequestResult(persistentGet));
+ return;
+ }
+ }
String identifier = persistentGet.getIdentifier();
if (downloadCallbacks.containsKey(identifier)) {
- /* ignore, because a download does not care about this. */
+ /* TODO */
return;
}
}
* @see net.pterodactylus.fcp.FcpListener#receivedPersistentPut(net.pterodactylus.fcp.FcpConnection,
* net.pterodactylus.fcp.PersistentPut)
*/
+ @SuppressWarnings("synthetic-access")
public void receivedPersistentPut(FcpConnection fcpConnection, PersistentPut persistentPut) {
- /* TODO */
+ if (fcpConnection != HighLevelClient.this.fcpConnection) {
+ return;
+ }
+ synchronized (syncObject) {
+ if (requestListCallback != null) {
+ RequestListResult requestListResult = requestListCallback.getIntermediaryResult();
+ requestListResult.addRequestResult(new PutRequestResult(persistentPut));
+ return;
+ }
+ }
}
/**
* @see net.pterodactylus.fcp.FcpListener#receivedPersistentPutDir(net.pterodactylus.fcp.FcpConnection,
* net.pterodactylus.fcp.PersistentPutDir)
*/
+ @SuppressWarnings("synthetic-access")
public void receivedPersistentPutDir(FcpConnection fcpConnection, PersistentPutDir persistentPutDir) {
- /* TODO */
+ if (fcpConnection != HighLevelClient.this.fcpConnection) {
+ return;
+ }
+ synchronized (syncObject) {
+ if (requestListCallback != null) {
+ RequestListResult requestListResult = requestListCallback.getIntermediaryResult();
+ requestListResult.addRequestResult(new PutDirRequestResult(persistentPutDir));
+ return;
+ }
+ }
}
/**
downloadCallback.progressUpdated();
return;
}
- /* unknown identifier? */
- logger.warning("unknown identifier for SimpleProgress: " + identifier);
+ HighLevelProgress highLevelProgress = new HighLevelProgress(identifier, simpleProgress.getTotal(), simpleProgress.getRequired(), simpleProgress.getSucceeded(), simpleProgress.getFailed(), simpleProgress.getFatallyFailed(), simpleProgress.isFinalizedTotal());
+ fireProgressReceived(identifier, highLevelProgress);
}
/**