Hi all,
I am working on AGDROID-11 [
https://issues.jboss.org/browse/AGDROID-11]. As suggested on
the JIRA, I started with investigating how AeroGear iOS handles pipe cancel operation.
-----------------------------------------------------------------------
---- Skip if you don't want to verify my iOS tracking ----
-----------------------------------------------------------------------
------------------------------ Start ---------------------------------
-----------------------------------------------------------------------
First, here is what I could track on the iOS side (please correct me if there's any
mistake):
- AGRESTPipe cancel method simply calls the cancelAllOperations on its restClient's
operationQueue [1].
- AGRESTPipe's restClient is a AGHttpClient [2].
- AGHttpClient is a AFHttpClient [3].
- AFHttpClient's operationQueue is a NSOperationQueue [4].
- Calling cancelAllOperations on NSOperationQueue sends cancel message to all Operations
in the queue [5]. If the operation is still in the queue (waiting to be executed), they
are not being executed. If the operation is alread started to it's execution, it's
up to the "Operation" how to respond to cancel message.
- Sending cancel message to operations sets their isCancelled flag. Any custom NSOperation
extension is recommended to handle this cancel operation [6].
- AGHttpClient creates the operation by calling HTTPRequestOperationWithRequest [7], which
is AFHttpClient method. It creates either custom (AFImage, AFJSON, AFPropertyList, AFXML)
operation [8] or AFHttpRequestOperation [9]. All custom operations are
AFHttpRequestOperation, which is a AFURLConnectionOperation [10].
- AFURLConnectionOperation implements cancel method [11] and simply cancels the
operation's NSURLConnection, the connection is cancelled and no delegate is called
[12].
- Then AFURLConnectionOperation calls its delegate method manually to finish the operation
[13].
- The delegate handles the case [14] and finishes the operation [15].
-----------------------------------------------------------------------
------------------------------- End ---------------------------------
-----------------------------------------------------------------------
---- Skip if you don't want to verify my iOS tracking ----
-----------------------------------------------------------------------
Simply, what I understand is, operation itself is responsible to handle the cancel message
sent by any pipe implementation. And on the Android side, the operations are runnables
[16, 17, 18, 19] as far as I could interpret. So, these runnables should be able to handle
cancel messages which means they should catch or throw the thrown InterruptedException.
So, the problem is, find a way to send cancel message to the tasks, and handle the cancel
message on the tasks.
HOW TO SEND CANCEL MESSAGE TO THE TASKS?
1ST PROPOSAL:
RestAdapter is using ThreadPoolExecutor [20]. So, one way to send cancel message to all
running and queued runnables is to call shutDownNow on the ThreadPoolExecutor [21]. It has
some limitations. First, shut down destroys the ThreadPoolExecutor, so it needs to be
recreated. Second, the current ThredPoolExecutor is static, means it will cancel all pipes
if it is shut down, so it needs to be an instance variable instead of static.
2ND PROPOSAL:
Instead of calling THREAD_POOL_EXECUTOR.execute(), if we call
THREAD_POOL_EXECUTOR.submit() [22], then it returns a Future representing the task. Future
has cancel() [23], which prevents running the task if it is not started, and interrupts
the task if it is currently running. Therefore, if we have a collection of <Future>
in RestAdapter (i.e. pipe), then we can cancel the operations by iterating over the
collection and calling cancel() on each Future. In this case, at some point, we need to
clean <Future> collection to get rid of the already cancelled tasks.
HOW TO HANDLE THE CANCEL MESSAGE ON THE TASKS?
I could only come up with a single solution, which is, adding another catch block to each
of the runnables [16, 17, 18, 19]. Here is what it will look like:
try {
this.result = ...
} catch (InterruptedException e) {
return; // don't call the callbacks, may need to check the cause of the
interrupt???
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
this.exception = e;
}
So, I would like to hear what you think about my solution, and if you have any
corrections, better solutions, comments, anything... All are most welcome.
Kind regards,
--- REFERENCES ---
[1]
https://github.com/aerogear/aerogear-ios/blob/master/AeroGear-iOS/AeroGea...
[2]
https://github.com/aerogear/aerogear-ios/blob/master/AeroGear-iOS/AeroGea...
[3]
https://github.com/aerogear/aerogear-ios/blob/master/AeroGear-iOS/AeroGea...
[4]
https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFH...
[5]
http://developer.apple.com/library/ios/DOCUMENTATION/Cocoa/Reference/NSOp...
[6]
http://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSOp...
[7]
https://github.com/aerogear/aerogear-ios/blob/master/AeroGear-iOS/AeroGea...
[8]
https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFH...
[9]
https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFH...
[10]
https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFH...
[11]
https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFU...
[12]
http://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foun...
[13]
https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFU...
[14]
https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFU...
[15]
https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFU...
[16]
https://github.com/aerogear/aerogear-android/blob/master/src/org/jboss/ae...
[17]
https://github.com/aerogear/aerogear-android/blob/master/src/org/jboss/ae...
[18]
https://github.com/aerogear/aerogear-android/blob/master/src/org/jboss/ae...
[19]
https://github.com/aerogear/aerogear-android/blob/master/src/org/jboss/ae...
[20]
https://github.com/aerogear/aerogear-android/blob/master/src/org/jboss/ae...
[21]
http://developer.android.com/reference/java/util/concurrent/ThreadPoolExe...
[22]
http://developer.android.com/reference/java/util/concurrent/AbstractExecu...
[23]
http://developer.android.com/reference/java/util/concurrent/Future.html#c...
---
Yavuz Selim Yilmaz
SUNY at Buffalo
Computer Science and Engineering
PhD Candidate