Invoke a a lambda function from a lambda function written in Java - java

Is there any good documentation how to invoke a lambda function from a lambda function written in java?
Is there a difference between invoking a lambda function from a lambda function or invoke from a normal java application?
The only thing that I found is the normal AWS Java SDK doc.
http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/index.html
Would be happy if someone could help me.

Yes, you can call lambdas normally from lambdas, just as you would if your code was executing on a host somewhere.
You'll have an additional step of ensuring that the lambda executing the call to another lambda has permissions to execute other lambda functions (the permission is called "lambda:InvokeFunction").
The rest of the steps are the same as regular java using the AWS SDK, you instantiate an AWSLambdaClient object, set your credentials and settings (region, etc.) and then create an InvokeRequest object which is the sub-lambda to be called with the appropriate payload.
Here's some sample java code which does exactly this, but it's also part of the standard sdk docs.
And also keep in mind that you'll still be subject to the original timeout for the first lambda, otherwise execution will be stopped.
Here's the relevant code snippets that you should be able to work from, I've made a change to how the second lamdba is called in terms of credentials - your can pass the credentials used to invoke the first lambda implicitly into the second one, which is probably a bit easier and more maintainable - you just need to ensure that the first lambda call gets the credentials and the rest will inherit them.
Region region;
AWSLambdaClient lambdaClient;
lambdaClient = new AWSLambdaClient(new DefaultAWSCredentialsProviderChain());
region = Region.getRegion(Regions.fromName(regionName));
lambdaClient.setRegion(region);
InvokeRequest invokeRequest = new InvokeRequest();
invokeRequest.setFunctionName(FunctionName);
invokeRequest.setPayload(ipInput);
returnDetails = byteBufferToString(
lambdaClient.invoke(invokeRequest).getPayload(),
Charset.forName("UTF-8"),logger);
edit: I should also point out that depending on your architecture, there's probably much cleaner options like using SQS, or depending on how simple your nested lambdas are, inlining them directly inside each other to avoid the extra call.

A lot of the used API functions are deprecated and the AWS docu is ... I share a new implemented example. The Lambda function "updateS3Chart" calls another Lambda function "AsyncUpdate" asynchronous:
public class LambdaInvoker {
static final Logger logger = LogManager.getLogger(LambdaInvoker.class);
static final String LambdaFunctionName = "AsyncUpdate";
private class AsyncLambdaHandler implements AsyncHandler<InvokeRequest, InvokeResult>
{
public void onSuccess(InvokeRequest req, InvokeResult res) {
logger.debug("\nLambda function returned:");
ByteBuffer response_payload = res.getPayload();
logger.debug(new String(response_payload.array()));
}
public void onError(Exception e) {
logger.debug(e.getMessage());
}
}
public void updateS3Chart(UpdateS3ChartRequest updateS3ChartRequest) {
Gson gson = new Gson();
try {
//issue: aws region is not set to debug-time. solution for eclipse:
//environment variable is set by lambda container or eclipse ide environment variables
//use instead for eclipse debugging: project -> Run as -> Run Configurations -> Environment -> Add variable: "AWS_REGION": "eu-central-1"
AWSLambdaAsync lambda = AWSLambdaAsyncClientBuilder.defaultClient(); //Default async client using the DefaultAWSCredentialsProviderChain and DefaultAwsRegionProviderChain chain
InvokeRequest req = new InvokeRequest()
.withFunctionName(LambdaFunctionName)
.withPayload(gson.toJson(updateS3ChartRequest));
Future<InvokeResult> future_res = lambda.invokeAsync(req, new AsyncLambdaHandler());
logger.debug("Waiting for async callback");
while (!future_res.isDone() && !future_res.isCancelled()) {
// perform some other tasks...
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
logger.debug("Thread.sleep() was interrupted!");
}
System.out.print(".");
}
} catch (Exception e) {
logger.fatal("Execute async lambda function: " + LambdaFunctionName + " failed: " + e.getMessage());
}
}
}
You have to set your AWS region as system property in your IDE for debugging (see comment in source code for Eclipse). UpdateS3ChartRequest is simple POJO with property set/get.

Related

How to error handle for 20 reactive async API calls in java?

I'm writing a service that calls 20 external vendor APIs, aggregates that data and writes it to a blob storage. This is how I am calling each api, after I am using Mono.zip() and writing that result into a blob storage. However I feel that the way I am writing the code is really redundant, specifically the error handling using .onErrorResume() and .doOnSuccess() Is there a way I can maek this code cleaner by using generics or utilizing inheritance some way? I just dont want hundreds of lines of code that are basically doing the same thing...
Mono<MailboxProvidersDTO> mailboxProvidersDTOMono = partnerAsyncService.asyncCallPartnerApi(getMailboxProvidersUrl, MailboxProvidersDTO.class)
.retryWhen(getRetrySpec())
//need to have error handling for all 20 api calls
.doOnSuccess(res -> {
log.info(Logger.EVENT_SUCCESS, "Mailbox Providers report successfully retrieved.");
res.setStatus("Success");
})
.onErrorResume(BusinessException.class, ex -> {
log.error(Logger.EVENT_FAILURE, ex.getMessage());
MailboxProvidersDTO audienceExplorerDTO = new MailboxProvidersDTO();
audienceExplorerDTO.setStatus("Failed");
return Mono.just(audienceExplorerDTO);
});
Mono<TimeZonesDTO> timeZonesDTOMono = partnerAsyncService.asyncCallPartnerApi(getTimeZonesUrl, TimeZonesDTO.class);
Mono<RegionsDTO> regionsDTOMono = partnerAsyncService.asyncCallPartnerApi(getRegionsUrl, RegionsDTO.class);
Mono<AudienceExplorerDataSourcesDTO> audienceExplorerDataSourcesDTOMono = partnerAsyncService.asyncCallPartnerApi(getAudienceExplorerDataSourcesUrl, AudienceExplorerDataSourcesDTO.class);
...
You can effectively use generics to refactor your code. You can couple functional interfaces and Generics to create what you need:
On your example, you need both to "setStatus" and create new instances of different classes. You could then create a utility function to add onSuccess/onFailure behaviours over your initial data fetching Mono:
public <T> Mono<T> withRecovery(Mono<T> fetchData, BiConsumer<T, String> setStatus, Supplier<T> createFallbackDto) {
return fetchData
.doOnSuccess(result -> {
log.info...
setStatus.accept(result, "Success");
})
.doOnError(BusinessException.class, err -> {
log.error...
T fallback = createFallbackDto.get();
setStatus.accept(fallback, "Error");
return Mono.just(fallback);
});
}
Then, you can use this method like that:
Mono<MailProvidersDto> mails = withRecovery(
partnersAsyncService.asyncCallPartnerApi(getMailboxProvidersUrl, MailboxProvidersDTO.class),
MailProvidersDto::setStatus,
MailProvidersDto::new
);
Mono<TimeZoneDto> timezone = withRecoery(
partnersAsyncService.asyncCallPartnerApi(getMailboxProvidersUrl, TimeZoneDto.class),
TimeZoneDto::setStatus,
TimeZoneDto::new
);
... // Repeat for each api
Notes:
If the setStatus method is available through a common interface that all DTO implement, you can get rid of the biconsumer, and directly call result.setStatus(String), by specializing T generic to T extends StatusInterface.
With it, you could also factorize initial fetching and retry calls, by passing related parameters (url, class, retry spec) as method input.

Pusher Chatkit Connect Callback in Java

I am trying to implement the Pusher ChatKit from the SDK site: https://docs.pusher.com/chatkit. I prefer to keep my code entirely in Java.
In the examples, there is this code block:
chatManager.connect { result ->
when (result) {
is Result.Success -> {
// We have connected!
currentUser = result.value // CurrentUser
}
is Result.Failure -> {
// Failure
handleConnectionError(result.error)
}
}
}
However, the above code is written in Kotlin. In my own code, mCurrentUser is null even after the chatManager.connect method runs.
I upgraded my Android Studio to run Java 8 and I'm trying to use the following code:
chatManager.connect(result -> {
if (result instanceof Result.Success) {
mCurrentUser = ((Result.Success<CurrentUser, Error>) result).getValue();
}
if (result instanceof Result.Failure) {
System.out.print(result);
}
return Unit.INSTANCE;
});
When I step through using the debugger, the entire block is skipped. The connect methods logs a successful connection, but result is always null. Can someone either show me how to write the same callback using a listener? Or, show me the issue with my lambda expressions.
By the way, chatManager.connect takes Function1<? super Result<CurrentUser, Error> Unit> callback as a parameter. Thanks in advance. I've been stuck on this for 2 days.

java Optional: if do something pattern

Is it able to write this code embedded into Optional method chain:
Optional<Application> appOpt = this.applicationDao.findById(application.getCode());
Application app = appOpt.orElse(Application::new);
if (!appOpt.isPresent()) {
app.get().setUserCreation("user");
app.get().setTimestampCreation(new Date());
}
I'd like to avoud using again the previous appOpt object. I would like to embed all this logic inside an Optional method chain.
I guess there should be another more elegant way to get it.
I've tried to play with ifPresent but it returns void and so I'm not able to chain orElse method:
appOpt.ifPresent(a -> {
a.setUserCreation("userCreation"));
app.setTimestampCreation(new Date());
})
.orElse(Application::new);
I hope I've explained so well.
Any ideas?
After looking again at your original code, it looks like the logic should run if the Optional is empty:
Application application = appOpt.orElseGet(() -> {
Application app = new Application();
app.setUserCreation("userCreation"));
app.setTimestampCreation(new Date());
return app;
});

How to use data obtained in a Java anonymous function that is inside of a method

Seems like a common issue, but I am just not sure of the solution.
I am using Vert.x with Java to call another service. I am using Vert.x's WebClient library. When I make the POST request, one of the arguments is an anonymous function. If the request succeeds, then I get my data.
The problem is getting this data outside of the anonymous function, in the rest of my method. I want my method to return the data obtained from my anonymous function. The data is in the body variable.
private JsonArray itemTypes() {
WebClient webClient = WebClient.create(vertx);
webCLient.post(80, "myApi.com", "/api/items")
.sendJsonObject(new JsonObject().put("mutator", "*"), ar -> {
if (ar.succeeded) {
HttpResponse<JsonArray> result = ar.result();
JsonArray body = result.body();
} else {
System.out.println(ar.cause().getMessage());
}
});
}
Right now, if I try to declare the body variable outside of the anonymous function and assign it inside (so that I can return the value), my IDE complains Local variable body defined in an enclosing scope must be final or effectively final.
I essentially want to do something like this:
private JsonArray itemTypes() {
JsonArray body;
WebClient webClient = WebClient.create(vertx);
webCLient.post(80, "myApi.com", "/api/items")
.sendJsonObject(new JsonObject().put("mutator", "*"), ar -> {
if (ar.succeeded) {
HttpResponse<JsonArray> result = ar.result();
body = result.body();
} else {
System.out.println(ar.cause().getMessage());
body = new JsonArray();
}
});
return body;
}
I've tried making the JsonArray body variable final, but that does not work. I've also tried to assign the whole webClient.post(....) call to a variable, but that did not work either.
How can I gain access to the body data outside of the anonymous function so I can return it in my method?
The problem you have is that WebClient appears to be completely asynchronous. This means that when you call .sendJsonObject, it basically sends the request and immediately returns; your code keeps running while WebClient waits for an answer.
Once a response is received, it calls the callback lambda you provided, possibly in some other thread. This may occur several seconds later and a lot of your other code may have had time to run in the meantime.
What you need is to have your main code wait for the response to be received. This could be done in several ways, one of which would be to create a CompletableFuture<T>, have the callback call the .complete(theValue) method and then have the outside code call .get() to wait for and receive the value.
In other words, the outside code would define and wait for the CompletableFuture<T> to complete, then have the .sendJsonObject callback complete it for you with the desired value.
Could do something like this:
private Future<JsonArray> itemTypes() {
Future<JsonArray> body = Future.future();
WebClient webClient =
WebClient.create(vertx);
webCLient.post(80, "myApi.com", "/api/items")
.sendJsonObject(new JsonObject().put("mutator", "*"), ar -> {
if (ar.succeeded) {
HttpResponse<JsonArray> result = ar.result();
body.complete(result.body());
} else {
System.out.println(ar.cause().getMessage());
body.fail(ar.cause());
}
});
return body;
}
Then set a handler on the returned future. You could also pass in the future with a handler instead, if you'd like:
private void itemTypes(Future<JsonArray> body) {

How to pass non-primitive Object from Java to JS by Android addJavascriptInterface?

I've had a trouble on this topic (for days).
For instance, basically, I want to pass a Java WebSocketClient object to Android Webview JS on demand, so wrote a code referring to:
http://foretribe.blogspot.com.au/2013/08/how-to-make-android-webview-support.html
https://github.com/thinksource/vw_websocket/blob/master/src/com/strumsoft/websocket/phonegap/WebSocketFactory.java
JAVA
wv.addJavascriptInterface(new WebSocketFactory(), "factoryJ");
public class WebSocketFactory {
//.............
public WebSocket getInstance(String url) {
socket = new WebSocket(new URI(url));
return socket;
}
}
JS
var url = "ws:someaddress";
var ws = factoryJ.getInstance(url);
console.log(ws.toString()) // object pointer displayed
console.log(ws.getReadyState()); //Uncaught Error: Error calling method on NPObject!
Uncaught Error: Error calling method on NPObject!
This concept does not work at least for me avove Android4.2+.,
because addJavascriptInterface() only works with Java primitive types and Strings.
cf)
Passing a JavaScript object using addJavascriptInterface() on Android
Error calling method on NPObject! in Android 2.2
As far as I know, the only way to pass JAVA object to JS is :
wv.addJavascriptInterface(JavaObject, "JsObject");
Sure, this should work fine as long as a passing JavaObject is pre-determined, but since WebSocket Object(s) is on-demand, I need to hack somehow for this.
So, I prepare JavaObject as some Array of WebSocket .
WebSocketNew[] ws = new WebSocketNew[99999];
wv.addJavascriptInterface(ws, "wsJ");
Unfortunately, the JS treats wsJ[n] as undefined ; appears it's also not allowed to pass ArrayObject.
I've read JSON or JSON array of Java can be passed, but it's string after all and cannot be this solution, am I correct?
Probably, back in old days, Android Java-JS interaction is implemented more freely, but under security issues, they restrict more (the annotation #JavascriptInterface is the one, and not only this but also other factors ) Android 4.2.1, WebView and javascript interface breaks
How to pass non-primitive Object from Java to JS by Android addJavascriptInterface?
Any thought?
Thanks.
You are correct that Javascript Interface methods can only return strings and primitive types. If you need to return a more complex object, you can try serializing the object to JSON. But, that only works for model-level objects. If you need to return a class which contains functionality, the only way I know of to do this is to wrap the class, and expose each of its methods as a #JavascriptInterface. For instance:
class Foo {
private WrappedClass myWrappedClass;
public Foo(WrappedClass classToWrap) {
myWrappedClass = classToWrap;
}
#JavascriptInterface
public String doSomething1() {
return myWrappedClass.doSomething1();
}
#JavascriptInterface
public int doSomething2() {
return myWrappedClass.doSomething2();
}
// Etc.
}
This is pretty tedious.
But, it looks like you're trying to use websockets. Have you considered Socket.io? It would allow you to use web sockets in Javascript, rather than relying on a JavascriptInterface.
http://socket.io
This has been one of the most hectic hack for me.
The major problem occurs from Android UI-thread and non-UI-thread bind with addJavascriptInterface
Firstly, my mention:
As far as I know, the only way to pass JAVA object to JS is :
wv.addJavascriptInterface(JavaObject, "JsObject");
is wrong.
In fact, I could pass WebSocketNew= my custom Java object properly to JS as return value of getInstance.
However, the object intended to pass/return to JS must be in scope of Android UI-thread.
If we just do return new WebSocketNew(new URI(url))), it's not found by JS, probably because JS runs in UI-thread, and the origin runs on non-UI-thread of addJavascriptInterface.
As I mentioned earlier, since WebScoket instance is on-demand, so I decided to create Object pool in UI-thread first.
This can be done with HashMap with the requested url String.
Java
final HashMap<String, WebSocketNew> ws = new HashMap<>();
//runs on non-UI-thread
class WebSocketFactory
{
public WebSocketFactory()
{
}
#JavascriptInterface
public WebSocketNew getInstance(String url)
{
System.out.println("============WebSocketFactory============getInstance " + url);
try
{
ws.put(url, new WebSocketNew(new URI(url)));
ws.get(url).connect();
System.out.println("=====WebSocketNew=====" + url + " " + ws.get(url).getReadyState());
return ws.get(url);
}
catch (Exception e)
{
System.out.println("==========ERROR");
return null;
}
}
}
wv.addJavascriptInterface(new WebSocketFactory(), "factoryJ");
wv.loadUrl("file:///android_asset/Content/app.html");
JS
window.WebSocket = function(url)
{
console.log('####################creating new WebScoketInstance JS ' + url);
var p = {
url: null
};
p.url = url;
var ws = factoryJ.getInstance(p.url);
var obj = {
send: function(data)
{
console.log('--- send: function(data)----- ws.send1(data);------');
ws.send1(data);
},
//.......
//.......
}
return obj;
};
The JS code is related to topic : JavaScript Event implementation to Closure based Object

Categories

Resources