Hello,
I am trying to create an ondemand plugin using atlassian-connect-express, nodejs, the JSON-RPC Confluence API and the async module for working with asynchronous JavaScript.
I have managed to get the user and group list from the target Confluence application using the JSON-RPC Confluence API. However when using the async.each method it never reaches the final callback function to be executed when all the iterations have been done.
Using this code it calls the first callback function for each iteration of the users array, but it never calls the final callback function.
//users: array of users //membershipList: multi dimensional array of users and its groups. async.each(users, function(currentuser, callback) { //For each user invokes getUserGroups console.log("Getting group list for user " + currentuser + " (getUserGroups JSON-RPC API)"); addon.httpClient(req).post({ uri: "/rpc/json-rpc/confluenceservice-v2/getUserGroups", json: [currentuser], userId: userId }, function(err, resp, body) { //First callback function for each iteration var jsonRpcBody = body; if (!err && !jsonRpcBody.error) { // If no errors in getUserGroups console.log("Groups found for user " + currentuser + ": " + JSON.stringify(body) ); // Do something with the user and its groups. UpdateUserMembershipList(currentuser,membershipList) } else { console.log("Error getting user membership ", err, jsonRpcBody.error); res.render('error', {error: err} ); } }); //getUserGroups }, function(err) { //Final callback function when all iterations are done if (err) return next(err); // When async iteration is done and all user group membership found console.log("User membership queries finished"); DoSomethingWithUserMembership(membershipList); //It never gets here }); //async
In the nodejs console I get the console.log output for every iteration, e.g.
Groups found for user admin: ["confluence-administrators","confluence-users"] Groups found for user testuser: ["confluence-users"]
However it never executes the line (No. 35 in the pasted code).
DoSomethingWithUserMembership(membershipList);
In the Confluence log it shows the error
2014-04-08 19:03:50,593 ERROR [http-1990-6] [plugin.module.confluence.MacroContentManager] getStaticContent Could not render macro -- url: /confluence/display/ds/Test+my+macro+4 | page: 852017 | userName: admin | referer: http://macbookserver.local:1990/confluence/dashboard.action | action: viewpage com.atlassian.plugin.connect.plugin.util.http.ContentRetrievalException: java.lang.RuntimeException: java.net.SocketTimeoutException at com.atlassian.plugin.connect.plugin.util.http.CachingHttpContentRetriever$FailFunction.apply(CachingHttpContentRetriever.java:243) at com.atlassian.plugin.connect.plugin.util.http.CachingHttpContentRetriever$FailFunction.apply(CachingHttpContentRetriever.java:227) at com.atlassian.util.concurrent.Promises$Of$2.apply(Promises.java:259) at com.atlassian.util.concurrent.Promises$Of$2.apply(Promises.java:256) at com.atlassian.util.concurrent.Promises$2.onFailure(Promises.java:162) at com.google.common.util.concurrent.Futures$7.run(Futures.java:1074) at com.google.common.util.concurrent.MoreExecutors$SameThreadExecutorService.execute(MoreExecutors.java:253) at com.google.common.util.concurrent.ExecutionList$RunnableExecutorPair.execute(ExecutionList.java:161) at com.google.common.util.concurrent.ExecutionList.execute(ExecutionList.java:146) at com.google.common.util.concurrent.AbstractFuture.done(AbstractFuture.java:235) at com.google.common.util.concurrent.AbstractFuture.setException(AbstractFuture.java:190) at com.google.common.util.concurrent.SettableFuture.setException(SettableFuture.java:68) at com.atlassian.util.concurrent.Promises$Of$2.apply(Promises.java:261) at com.atlassian.util.concurrent.Promises$Of$2.apply(Promises.java:256) at com.atlassian.util.concurrent.Promises$2.onFailure(Promises.java:162) at com.google.common.util.concurrent.Futures$7.run(Futures.java:1074) at com.google.common.util.concurrent.MoreExecutors$SameThreadExecutorService.execute(MoreExecutors.java:253) at com.google.common.util.concurrent.ExecutionList$RunnableExecutorPair.execute(ExecutionList.java:161) at com.google.common.util.concurrent.ExecutionList.execute(ExecutionList.java:146) at com.google.common.util.concurrent.AbstractFuture.done(AbstractFuture.java:235) at com.google.common.util.concurrent.AbstractFuture.setException(AbstractFuture.java:190) at com.google.common.util.concurrent.SettableFuture.setException(SettableFuture.java:68) at com.atlassian.httpclient.apache.httpcomponents.SettableFuturePromiseHttpPromiseAsyncClient$1$2.run(SettableFuturePromiseHttpPromiseAsyncClient.java:59) at com.atlassian.httpclient.apache.httpcomponents.SettableFuturePromiseHttpPromiseAsyncClient$ThreadLocalDelegateRunnable$1.run(SettableFuturePromiseHttpPromiseAsyncClient.java:197) at com.atlassian.httpclient.apache.httpcomponents.SettableFuturePromiseHttpPromiseAsyncClient.runInContext(SettableFuturePromiseHttpPromiseAsyncClient.java:90) at com.atlassian.httpclient.apache.httpcomponents.SettableFuturePromiseHttpPromiseAsyncClient$ThreadLocalDelegateRunnable.run(SettableFuturePromiseHttpPromiseAsyncClient.java:192) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918) at java.lang.Thread.run(Thread.java:680) Caused by: java.lang.RuntimeException: java.net.SocketTimeoutException at com.google.common.base.Throwables.propagate(Throwables.java:156) at com.atlassian.httpclient.apache.httpcomponents.ApacheAsyncHttpClient$5.apply(ApacheAsyncHttpClient.java:335) at com.atlassian.httpclient.apache.httpcomponents.ApacheAsyncHttpClient$5.apply(ApacheAsyncHttpClient.java:329) at com.atlassian.util.concurrent.Promises$Of$2.apply(Promises.java:259) ... 16 more Caused by: java.net.SocketTimeoutException at org.apache.http.nio.protocol.HttpAsyncRequestExecutor.timeout(HttpAsyncRequestExecutor.java:279) at org.apache.http.impl.nio.client.LoggingAsyncRequestExecutor.timeout(LoggingAsyncRequestExecutor.java:128) at org.apache.http.impl.nio.DefaultHttpClientIODispatch.onTimeout(DefaultHttpClientIODispatch.java:136) at org.apache.http.impl.nio.DefaultHttpClientIODispatch.onTimeout(DefaultHttpClientIODispatch.java:49) at org.apache.http.impl.nio.reactor.AbstractIODispatch.timeout(AbstractIODispatch.java:169) at org.apache.http.impl.nio.reactor.BaseIOReactor.sessionTimedOut(BaseIOReactor.java:257) at org.apache.http.impl.nio.reactor.AbstractIOReactor.timeoutCheck(AbstractIOReactor.java:494) at org.apache.http.impl.nio.reactor.BaseIOReactor.validate(BaseIOReactor.java:207) at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:284) at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:106) at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:604) ... 1 more
Any clues about the java.net.SocketTimeoutException error?
Any other solution to iterate the users array requesting the groups of each user and waiting for all users being processed (preferably in a non blocking way) before processing all the resulting information?
Thanks
Community moderators have prevented the ability to post new answers.
The SocketTimeoutException is occurring because Confluence has determined that your macro is not returning data within the allotted timeframe.
Here are some options:
- Make your code run/return faster - I'm not being facetious here :)
- Convert your macro to immediately return a "loading" function which makes an asynchronous request back to your add-on and retrieve data as it is ready. You could consider using websockets here if the data is big.
Regarding your initial question about async#each. Reading the documentation:
iterator(item, callback)
- A function to apply to each item in arr
. The iterator is passed a callback(err)
which must be called once it has completed. If no error has occured, the callback
should be run without arguments or with an explicit null
argument.You need to invoke the callback yourself from each iteration. Not doing so means that the async module doesn't know when all the iterations have completed, and thus your "un-ending" request.
Hello Seb,
You are quite right, I forgot to call the callback function. I will change my code and post the solution here, in case it helps others.
Thanks a lot for your help.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Seb,
As you said, including callback() after line 21 it works fine, no more SocketTimeoutException and the final callback function is called. Full code would be
//users: array of users //membershipList: multi dimensional array of users and its groups. async.each(users, function(currentuser, callback) { //For each user invokes getUserGroups console.log("Getting group list for user " + currentuser + " (getUserGroups JSON-RPC API)"); addon.httpClient(req).post({ uri: "/rpc/json-rpc/confluenceservice-v2/getUserGroups", json: [currentuser], userId: userId }, function(err, resp, body) { //First callback function for each iteration var jsonRpcBody = body; if (!err && !jsonRpcBody.error) { // If no errors in getUserGroups console.log("Groups found for user " + currentuser + ": " + JSON.stringify(body) ); // Do something with the user and its groups. UpdateUserMembershipList(currentuser,membershipList) callback(); } else { console.log("Error getting user membership ", err, jsonRpcBody.error); res.render('error', {error: err} ); } }); //getUserGroups }, function(err) { //Final callback function when all iterations are done if (err) return next(err); // When async iteration is done and all user group membership found console.log("User membership queries finished"); DoSomethingWithUserMembership(membershipList); }); //async
Thanks again for your help.
Cheers,
Enrique.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.