Skip to content

Commit 2b060da

Browse files
committed
Moving cognito async functions and lambda async invoke to background threads.
1 parent 2e91bee commit 2b060da

File tree

3 files changed

+260
-37
lines changed

3 files changed

+260
-37
lines changed

Core/android/src/main/java/com/amazonaws/reactnative/core/AWSRNCognitoCredentials.java

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public class AWSRNCognitoCredentials extends ReactContextBaseJavaModule {
6363
private static final String DIGITSPROVIDER = "www.digits.com";
6464
private CognitoCachingCredentialsProvider credentialsProvider;
6565

66+
private final BackgroundRunner backgroundRunner = new BackgroundRunner();
6667

6768
public AWSRNCognitoCredentials(ReactApplicationContext reactContext) {
6869
super(reactContext);
@@ -111,37 +112,51 @@ public void setLogins(final ReadableMap logins) {
111112

112113
@ReactMethod
113114
public void getIdentityIDAsync(final Promise promise) {
114-
try {
115-
final String identityId = credentialsProvider.getIdentityId();
116-
if (identityId != null) {
117-
final WritableMap identityidMap = Arguments.createMap();
118-
identityidMap.putString(IDENTITYID, identityId);
119-
promise.resolve(identityidMap);
120-
}
121-
} catch (AmazonServiceException e) {
122-
promise.reject(e.getErrorCode(), e.getErrorMessage(), e);
123-
} catch (AmazonClientException e) {
124-
promise.reject("", e.getMessage(), e);
125-
}
115+
backgroundRunner.runInBackground(
116+
new BackgroundRunner.Supplier<WritableMap>() {
117+
@Override
118+
public WritableMap get() throws Exception {
119+
final String identityId = credentialsProvider.getIdentityId();
120+
if (identityId != null) {
121+
final WritableMap identityidMap = Arguments.createMap();
122+
identityidMap.putString(IDENTITYID, identityId);
123+
return identityidMap;
124+
}
125+
else {
126+
// TODO: throw exception instead?
127+
return null;
128+
}
129+
}
130+
},
131+
PROMISE_REJECTOR,
132+
promise
133+
);
126134
}
127135

128136
@ReactMethod
129137
public void getCredentialsAsync(final Promise promise) {
130-
try {
131-
final AWSSessionCredentials cred = credentialsProvider.getCredentials();
132-
if (cred != null) {
133-
final WritableMap credentials = Arguments.createMap();
134-
credentials.putString(ACCESS_KEY, cred.getAWSAccessKeyId());
135-
credentials.putString(SESSION_KEY, cred.getSessionToken());
136-
credentials.putString(SECRET_KEY, cred.getAWSSecretKey());
137-
credentials.putString(EXPIRATION, credentialsProvider.getSessionCredentitalsExpiration().toString());
138-
promise.resolve(credentials);
139-
}
140-
} catch (AmazonServiceException e) {
141-
promise.reject(e.getErrorCode(), e.getErrorMessage(), e);
142-
} catch (AmazonClientException e) {
143-
promise.reject("", e.getMessage(), e);
144-
}
138+
backgroundRunner.runInBackground(
139+
new BackgroundRunner.Supplier<WritableMap>() {
140+
@Override
141+
public WritableMap get() throws Exception {
142+
final AWSSessionCredentials cred = credentialsProvider.getCredentials();
143+
if (cred != null) {
144+
final WritableMap credentials = Arguments.createMap();
145+
credentials.putString(ACCESS_KEY, cred.getAWSAccessKeyId());
146+
credentials.putString(SESSION_KEY, cred.getSessionToken());
147+
credentials.putString(SECRET_KEY, cred.getAWSSecretKey());
148+
credentials.putString(EXPIRATION, credentialsProvider.getSessionCredentitalsExpiration().toString());
149+
return credentials;
150+
}
151+
else {
152+
// TODO: throw exception instead?
153+
return null;
154+
}
155+
}
156+
},
157+
PROMISE_REJECTOR,
158+
promise
159+
);
145160
}
146161

147162
@ReactMethod
@@ -194,4 +209,18 @@ private String keyConverter(final String key) {
194209
return key;
195210
}
196211
}
212+
213+
private static final BackgroundRunner.PromiseRejector PROMISE_REJECTOR = new BackgroundRunner.PromiseRejector() {
214+
@Override
215+
public void reject(Exception e, Promise promise) {
216+
if(e instanceof AmazonServiceException) {
217+
AmazonServiceException ase = (AmazonServiceException)e;
218+
promise.reject(ase.getErrorCode(), ase.getErrorMessage(), e);
219+
}
220+
else {
221+
promise.reject("", e.getMessage(), e);
222+
}
223+
}
224+
};
225+
197226
}
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
//
2+
// Copyright 2010-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License").
5+
// You may not use this file except in compliance with the License.
6+
// A copy of the License is located at
7+
//
8+
// http://aws.amazon.com/apache2.0
9+
//
10+
// or in the "license" file accompanying this file. This file is distributed
11+
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
// express or implied. See the License for the specific language governing
13+
// permissions and limitations under the License.
14+
//
15+
16+
package com.amazonaws.reactnative.core;
17+
18+
import android.os.AsyncTask;
19+
20+
import com.facebook.react.bridge.Promise;
21+
22+
/**
23+
* Utility for executing tasks on a background thread and providing output/failure notifications via a Promise bridge.
24+
*/
25+
public class BackgroundRunner {
26+
27+
// NOTE: wanted these to be lambdas but I couldn't get the jack toolchain enabled :[
28+
// https://developer.android.com/guide/platform/j8-jack.html
29+
30+
public static interface Supplier<OUTPUT> {
31+
/**
32+
* Instructs the supplier to produce and return its output.
33+
*/
34+
OUTPUT get() throws Exception;
35+
}
36+
37+
public static interface Function<INPUT, OUTPUT> {
38+
/**
39+
* Instructs the function to produce and return its output.
40+
*/
41+
OUTPUT apply(INPUT input) throws Exception;
42+
}
43+
44+
public static interface PromiseRejector {
45+
/**
46+
* Rejects the given promise in the manner most appropriate for the encountered failure.
47+
*/
48+
void reject(Exception e, Promise promise);
49+
}
50+
51+
/**
52+
* Executes the given function in the background and resolves the promise with its output, or rejects the promise
53+
* with any error encountered.
54+
*
55+
* @param backgroundTask the function to execute in the background.
56+
* @param promise the bridge instance to update with the results of the function. If the function succeeds, its
57+
* output is forwarded to the promise's {@link Promise#resolve resolve} method. Otherwise, the caught
58+
* exception is passed to the promise's {@link Promise#reject reject} method.
59+
*/
60+
public <OUTPUT> void runInBackground(Supplier<OUTPUT> backgroundTask, Promise promise) {
61+
runInBackground(backgroundTask, DEFAULT_REJECTOR, promise);
62+
}
63+
64+
/**
65+
* Executes the given function in the background and resolves the promise with its output, or rejects the promise
66+
* with any error encountered.
67+
*
68+
* @param backgroundTask the function to execute in the background.
69+
* @param promiseRejector invokes the promise reject overload most appropriate for the given failure.
70+
* @param promise the bridge instance to update with the results of the function. If the function succeeds, its
71+
* output is forwarded to the promise's {@link Promise#resolve resolve} method. Otherwise, the caught
72+
* exception is passed to the promise's {@link Promise#reject reject} method.
73+
*/
74+
public <OUTPUT> void runInBackground(
75+
Supplier<OUTPUT> backgroundTask,
76+
PromiseRejector promiseRejector,
77+
Promise promise
78+
) {
79+
runInBackground(
80+
SUPPLIER_BRIDGE,
81+
promiseRejector,
82+
(Supplier<Object>)backgroundTask, // technically, this erases OUTPUT but that's OK
83+
promise
84+
);
85+
}
86+
87+
/**
88+
* Variant of {@link #runInBackground(Supplier,Promise)} that allows you to re-use a generic task definition.
89+
* <p>
90+
* Executes the given function in the background and resolves the promise with its output, or rejects the promise
91+
* with any error encountered.
92+
*
93+
* @param backgroundTask the function to execute in the background.
94+
* @param input the input data to provide to the {@code backgroundTask} function. Can be {@code null}.
95+
* @param promise the bridge instance to update with the results of the function. If the function succeeds, its
96+
* output is forwarded to the promise's {@link Promise#resolve resolve} method. Otherwise, the caught
97+
* exception is passed to the promise's {@link Promise#reject reject} method.
98+
*/
99+
public <INPUT, OUTPUT> void runInBackground(
100+
Function<INPUT, OUTPUT> backgroundTask,
101+
INPUT input,
102+
Promise promise
103+
) {
104+
runInBackground(backgroundTask, DEFAULT_REJECTOR, input, promise);
105+
}
106+
107+
/**
108+
* Variant of {@link #runInBackground(Supplier,Promise)} that allows you to re-use a generic task definition.
109+
* <p>
110+
* Executes the given function in the background and resolves the promise with its output, or rejects the promise
111+
* with any error encountered.
112+
*
113+
* @param backgroundTask the function to execute in the background.
114+
* @param promiseRejector invokes the promise reject overload most appropriate for the given failure.
115+
* @param input the input data to provide to the {@code backgroundTask} function. Can be {@code null}.
116+
* @param promise the bridge instance to update with the results of the function. If the function succeeds, its
117+
* output is forwarded to the promise's {@link Promise#resolve resolve} method. Otherwise, the caught
118+
* exception is passed to the promise's {@link Promise#reject reject} method.
119+
*/
120+
public <INPUT, OUTPUT> void runInBackground(
121+
Function<INPUT, OUTPUT> backgroundTask,
122+
PromiseRejector promiseRejector,
123+
INPUT input,
124+
Promise promise
125+
) {
126+
if(backgroundTask == null) throw new NullPointerException("backgroundTask is required.");
127+
if(promiseRejector == null) throw new NullPointerException("promiseRejector is required.");
128+
if(promise == null) throw new NullPointerException("promise is required.");
129+
130+
(new TaskRunner(backgroundTask, promiseRejector, promise)).execute(input);
131+
}
132+
133+
private static class BackgroundTaskResult<T> {
134+
public Exception failure;
135+
public T result;
136+
}
137+
138+
private static class TaskRunner<INPUT, OUTPUT> extends AsyncTask<INPUT, Void, BackgroundTaskResult<OUTPUT>> {
139+
private final Function<INPUT, OUTPUT> backgroundTask;
140+
private final PromiseRejector promiseRejector;
141+
private final Promise promise;
142+
143+
public TaskRunner(
144+
Function<INPUT, OUTPUT> backgroundTask,
145+
PromiseRejector promiseRejector,
146+
Promise promise
147+
) {
148+
this.backgroundTask = backgroundTask;
149+
this.promiseRejector = promiseRejector;
150+
this.promise = promise;
151+
}
152+
153+
@Override
154+
protected BackgroundTaskResult<OUTPUT> doInBackground(INPUT... input) {
155+
BackgroundTaskResult<OUTPUT> result = new BackgroundTaskResult<>();
156+
try {
157+
result.result = backgroundTask.apply(input[0]);
158+
}
159+
catch(Exception e) {
160+
result.failure = e;
161+
e.printStackTrace();
162+
}
163+
return result;
164+
}
165+
166+
@Override
167+
protected void onPostExecute(BackgroundTaskResult<OUTPUT> result) {
168+
if(result.failure != null)
169+
promiseRejector.reject(result.failure, promise);
170+
else
171+
promise.resolve(result.result);
172+
}
173+
174+
}
175+
176+
private static final Function<Supplier<Object>, Object> SUPPLIER_BRIDGE = new Function<Supplier<Object>, Object>() {
177+
@Override
178+
public Object apply(Supplier<Object> input) throws Exception { return input.get(); }
179+
};
180+
181+
private static final PromiseRejector DEFAULT_REJECTOR = new PromiseRejector() {
182+
@Override
183+
public void reject(Exception e, Promise promise) { promise.reject(e); }
184+
};
185+
186+
}

Lambda/android/src/main/java/com/amazonaws/reactnative/lambda/AWSRNLambdaClient.java

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import com.amazonaws.reactnative.core.AWSRNClientConfiguration;
1919
import com.amazonaws.reactnative.core.AWSRNClientMarshaller;
2020
import com.amazonaws.reactnative.core.AWSRNCognitoCredentials;
21+
import com.amazonaws.reactnative.core.BackgroundRunner;
22+
import com.amazonaws.reactnative.core.BackgroundRunner.Supplier;
2123
import com.amazonaws.regions.Region;
2224
import com.amazonaws.regions.Regions;
2325
import com.amazonaws.services.lambda.*;
@@ -36,12 +38,13 @@
3638

3739
import java.nio.ByteBuffer;
3840

39-
4041
public class AWSRNLambdaClient extends ReactContextBaseJavaModule {
4142

4243
private AWSLambdaClient lambdaClient;
4344
private Gson gson;
4445

46+
private final BackgroundRunner backgroundRunner = new BackgroundRunner();
47+
4548
public AWSRNLambdaClient(final ReactApplicationContext context) {
4649
super(context);
4750
}
@@ -67,15 +70,20 @@ public void initWithOptions(final ReadableMap options) {
6770

6871
@ReactMethod
6972
public void Invoke(final ReadableMap options, final Promise promise) {
70-
try {
71-
final InvokeRequest request = gson.fromJson(new JSONObject(AWSRNClientMarshaller.readableMapToMap(options)).toString(), InvokeRequest.class);
72-
final InvokeResult response = lambdaClient.invoke(request);
73-
final WritableMap map = AWSRNClientMarshaller.jsonToReact(new JSONObject(gson.toJson(response)));
74-
promise.resolve(map);
75-
} catch (Exception e) {
76-
promise.reject(e);
77-
return;
78-
}
73+
backgroundRunner.runInBackground(
74+
new Supplier<WritableMap>() {
75+
@Override
76+
public WritableMap get() throws Exception {
77+
final InvokeRequest request = gson.fromJson(
78+
new JSONObject(AWSRNClientMarshaller.readableMapToMap(options)).toString(),
79+
InvokeRequest.class
80+
);
81+
final InvokeResult response = lambdaClient.invoke(request);
82+
return AWSRNClientMarshaller.jsonToReact(new JSONObject(gson.toJson(response)));
83+
}
84+
},
85+
promise
86+
);
7987
}
8088

8189
}

0 commit comments

Comments
 (0)