How to call multiple completablefuture concurrently after main completable future completed - java-8

How to enhance function below, where getB, getC, getD have dependency with A, and required to wait A to complete before call.
However i wish to call B C D concurrently after A completed.
Thankyou for all the helps
note: all dbService return a completableFuture
CompletableFuture<List<A>> a= dbService.getA(request);
a.thenApply(a-> {
try {
return dbService.getB(a);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
})
.thenAccept (result->{
//do something with B
});
a.thenApply(a-> {
try {
return dbService.getC(a);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
})
.thenAccept (result->{
//do something with C
});
a.thenApply(a-> {
try {
return dbService.getD(a);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
})
.thenAccept (result->{
//do something with D
});

If you don't care about the returns of B, C, D then:
CompletionStage<List<A>> aFuture = dbService.getA(request);
aFuture.whenCompleteAsync((a, ex) -> {
if (ex != null) {
dbService.getB(a);
// ...
} else {
// ...
}
});
aFuture.whenCompleteAsync((a, ex) -> {
if (ex != null) {
dbService.getC(a);
// ...
} else {
// ...
}
});
aFuture.whenCompleteAsync((a, ex) -> {
if (ex != null) {
dbService.getD(a);
// ...
} else {
// ...
}
});
Your program will return to the main event loop as soon as dbService.getA(request) kicks off, i.e. before it even completes. And when that does complete, each of the 3 blocks execute (due to having been queued by whenComplete) on different threads (than main and each other, due to the Async).
If you do want to do something with the returns of B, C, and D, then:
CompletionStage<List<A>> aFuture = dbService.getA(request);
CompletionStage<B> bFuture = aFuture.thenApplyAsync(a -> {
return dbService.getB(a);
});
CompletionStage<C> cFuture = aFuture.thenApplyAsync(a -> {
return dbService.getC(a);
});
CompletionStage<D> dFuture = aFuture.thenApplyAsync(a -> {
return dbService.getD(a);
});
CompletionStage<Something> sFuture = bFuture.thenCompose(b -> {
cFuture.thenCompose(c -> {
dFuture.thenApply(d -> {
// do something with b, c, d
return new Something();
});
});
});
You can shorten this using thenCombine, which is like a thenApply that waits on 2 futures:
CompletionStage<List<A>> aFuture = dbService.getA(request);
CompletionStage<B> bFuture = aFuture.thenApplyAsync(a -> {
return dbService.getB(a);
});
CompletionStage<C> cFuture = aFuture.thenApplyAsync(a -> {
return dbService.getC(a);
});
CompletionStage<D> dFuture = aFuture.thenApplyAsync(a -> {
return dbService.getD(a);
});
CompletionStage<Something> sFuture = bFuture.thenCompose(b -> {
cFuture.thenCombine(dFuture, (c, d) -> {
// do something with b, c, d
return new Something();
});
});
To extend this to any number of concurrent stages, I like to use Spotify's CompletableFutures package like this:
CompletionStage<List<A>> aFuture = dbService.getA(request);
CompletionStage<B> bFuture = aFuture.thenApplyAsync(a -> {
return dbService.getB(a);
});
CompletionStage<C> cFuture = aFuture.thenApplyAsync(a -> {
return dbService.getC(a);
});
CompletionStage<D> dFuture = aFuture.thenApplyAsync(a -> {
return dbService.getD(a);
});
CompletionStage<Something> sFuture = CompletableFutures.combine((bFuture, cFuture, dFuture) -> {
// do something with b, c, d
return new Something();
}

Related

Webclient retryWhen and onErrorResume mutually exclusive?

I am trying to implement a retry on specific exception but I could not make it work using the following:
return client
.sendWebhook(request, url)
.exchangeToMono(
response -> {
final HttpStatus status = response.statusCode();
return response
.bodyToMono(String.class)
.defaultIfEmpty(StringUtils.EMPTY)
.map(
body -> {
if (status.is2xxSuccessful()) {
log.info("HTTP_SUCCESS[{}][{}] body[{}]", functionName, company, body);
return ResponseEntity.ok().body(body);
} else {
log.warn(
format(
"HTTP_ERROR[%s][%s] status[%s] body[%s]",
functionName, company, status, body));
return status.is4xxClientError()
? ResponseEntity.badRequest().body(body)
: ResponseEntity.internalServerError().body(body);
}
});
})
.retryWhen(
Retry.backoff(1, Duration.ofSeconds(1))
.filter(
err -> {
if (err instanceof PrematureCloseException) {
log.warn("PrematureCloseException detected retrying.");
return true;
}
return false;
}))
.onErrorResume(
ex -> {
log.warn(
format(
"HTTP_ERROR[%s][%s] errorInternal[%s]",
functionName, company, ex.getMessage()));
return Mono.just(ResponseEntity.internalServerError().body(ex.getMessage()));
});
It seems that the retry is never getting called on PrematureCloseException.
Resolved, it was not working because of rootCause
Retry.backoff(3, Duration.ofMillis(500))
.filter(
ex -> {
if (ExceptionUtils.getRootCause(ex) instanceof PrematureCloseException) {
log.info(
"HTTP_RETRY[{}][{}] PrematureClose detected retrying", functionName, company);
return true;
}
return false;
});

Graphql - universal permission guards

I am trying to implement permission guards in a graphql backend using apollo server. The following code works:
Resolvers
const Notification = require('../../database/models/notifications');
const Task = require('../../database/models/tasks');
notification: combineResolvers(isNotificationOwner, async (_, { id }) => {
try {
const notification = await Notification.findById(id);
return notification;
} catch (error) {
throw error;
}
})
task: combineResolvers(isTaskOwner, async (_, { id }) => {
try {
const task = await Task.findById(id);
return task;
} catch (error) {
throw error;
}
})
Resolver Middleware (permission guards)
const Notification = require('../../database/models/notifications');
const Task = require('../../database/models/tasks');
// userId is the id of the logged in user retrieved from the context
module.exports.isNotificationOwner = async (_, { id }, { userId }) => {
try {
const notification = await Notification.findById(id);
if (notification.user.toString() !== userId) {
throw new ForbiddenError('You are not the owner');
}
return skip;
} catch (error) {
throw error;
}
}
module.exports.isTaskOwner = async (_, { id }, { userId }) => {
try {
const task = await Task.findById(id);
if (task.user.toString() !== userId) {
throw new ForbiddenError('You are not the owner');
}
return skip;
} catch (error) {
throw error;
}
}
Going on like this will create a lot of duplicate code and does not feel very DRY. Therefore, I am trying to create a more universal solution, to no evail so far.
What I tried:
Resolvers
const Notification = require('../../database/models/notifications');
const Task = require('../../database/models/tasks');
notification: combineResolvers(isOwner, async (_, { id }) => {
try {
const notification = await Notification.findById(id);
return notification;
} catch (error) {
throw error;
}
})
task: combineResolvers(isOwner, async (_, { id }) => {
try {
const task = await Task.findById(id);
return task;
} catch (error) {
throw error;
}
})
Resolver Middleware
const Notification = require('../../database/models/notifications');
const Task = require('../../database/models/tasks');
module.exports.isOwner = async (_, { id, collection }, { userId }) => {
try {
const document = await collection.findById(id);
if (document.user.toString() !== userId) {
throw new ForbiddenError('You are not the owner');
}
return skip;
} catch (error) {
throw error;
}
}
I am unable to pass a collection name as an argument to the middleware resolver.
I would be tremendously thankful for any kind of help!
Based off your code, it seems like you're looking for making isOwner a higher-order function, so that you can pass in the collection, and it returns the curried method.
module.exports.isOwner = (collection) => {
return async (_, { id }, { userId }) => {
try {
const document = await collection.findById(id);
if (document.user.toString() !== userId) {
throw new ForbiddenError('You are not the owner');
}
return skip;
} catch (error) {
throw error;
}
}
}
Usage:
const resolvers = {
Query: {
task: combineResolvers(isOwner(Task), async (_, { id }) => {
try {
const task = await Task.findById(id);
return task;
} catch (error) {
throw error;
}
})
},
};

Zoho Deluge: how to display the type of an object or variable?

I have a variable in a deluge script and I want to display its data type in the log. I tried this, which reports there is no function type(): info type(my variable);
Also I searched for zoho deluge data type and zoho deluge introspection but didn't find anything that was actionable.
Any suggestions will be appreciated.
Thank you.
2020-09-25 update:
In the online deluge editor, the tooltips will display the type of a variable as one enters the variable name. That means the javascript in the editor is able to get the variable types and display them. Is there a deluge command to do the same thing?
If you are looking for a function to fetch the variable data type in Zoho Creator, I have written one for you:
string type(list var)
{
v = var.get(0);
if(v == null)
{
return "NULL";
}
try
{
m = v.toMap();
if(m != null)
{
return "MAP";
}
}
catch (e)
{
}
try
{
m = v.get(0);
if(m != null)
{
return "LIST";
}
}
catch (e)
{
}
try
{
if(isNumber(v))
{
return "NUMBER";
}
}
catch (e)
{
}
try
{
if(isText(v))
{
return "STRING";
}
}
catch (e)
{
}
try
{
if(isDate(v))
{
return "DATE";
}
}
catch (e)
{
}
try
{
if(isFile(v))
{
return "FILE";
}
}
catch (e)
{
}
try
{
if(isNull(v))
{
return "NULL";
}
}
catch (e)
{
}
try
{
if(v.isEmpty())
{
try
{
v.add(1);
return "LIST";
}
catch (e)
{
}
return "MAP";
}
}
catch (e)
{
}
return "COLLECTION";
}
And you can use the following function to test it:
void testType()
{
info thisapp.type({50});
info thisapp.type({"ABC"});
info thisapp.type({"01-01-2010"});
info thisapp.type({'01-01-2010'});
info thisapp.type({{1,2,3}});
info thisapp.type({{"a":"b"}});
x = List();
info thisapp.type({x});
x = Map();
info thisapp.type({x});
csv_file = "\"Name\",\"Age\"\n\"Mathew\",\"20\"".tofile("sample.csv");
info thisapp.type({csv_file});
info thisapp.type({null});
products = Collection("Creator":5,"CRM":2,"Mail":8);
info thisapp.type({products});
}
Have you tried just:
info myvariable;
It is not possible to get the data type.

redux-saga, websockets and actions queue

I have the following problem: server send's messages to the client through websocket. on the client, I need to display this messages to the user. but the problem is that sometimes messages come to fast, and I need to organize some sort of queue and show that messages one after another.
my saga:
import { eventChannel, effects, takeEvery } from 'redux-saga';
import { types, actionCreators } from './actions';
const { call, put, take, race } = effects;
function watchMessages(socket) {
return eventChannel((emitter) => {
socket.onopen = (e) => (emitter(actionCreators.socketOpen(e)));
socket.onclose = (e) => (emitter(actionCreators.socketClose(e)));
socket.onerror = (e) => (emitter(actionCreators.socketError(e)));
socket.onmessage = (e) => (emitter(actionCreators.socketMessage(e)));
return () => {
socket.close();
};
});
}
function* internalListener(socket) {
while (true) {
const data = yield take(types.SOCKET_SEND);
socket.send(data.payload);
}
}
function* externalListener(socketChannel) {
while (true) {
const action = yield take(socketChannel);
yield put(action);
}
}
function* wsHandling(action) {
const socket = action.payload.socket;
while (true) {
const socketChannel = yield call(watchMessages, socket);
const { cancel } = yield race({
task: [call(externalListener, socketChannel), call(internalListener, socket)],
cancel: take(types.SOCKET_CLOSE),
});
if (cancel) {
socketChannel.close();
}
}
}
export default function* rootSaga(action) {
yield takeEvery(types.SOCKET_CONNECT, wsHandling);
}
my reducer:
function dataReducer(state = initialStateData, action) {
switch (action.type) {
case types.SOCKET_MESSAGE:
if (action.payload.channel === 'channel1') {
return state
.set('apichannel1', action.payload);
} else if (action.payload.channel === 'channel2') {
return state
.set('apichannel2', action.payload);
} else if (action.payload.channel === 'channel3') {
return state
.set('apichannel3', action.payload);
}
return state;
default:
return state;
}
}
so now, when the new message arrives, I'm changing state and just display it on the screen.
any ideas how I can turn this into the following: put arrived messages into some sort of queue, and show them one by one on screen for some custom time?
You can buffer (queue) actions in redux saga using the actionChannel effect.
https://redux-saga.js.org/docs/advanced/Channels.html#using-the-actionchannel-effect
Then you can read from the channel's buffer at your own speed.
I created a simplified example that listens for messages and displays max 3 at a time, each for 5 seconds. See:
https://codesandbox.io/s/wt8uu?file=/src/index.js
At the bottom, there is a console panel, use it to call addMsg('Msg: ' + Math.random()) to simulate new socket.io message.
so I did it this way, here is the code, maybe it will be useful for someone
let pendingTasks = [];
let activeTasks = [];
function watchMessages(socket) {
return eventChannel((emitter) => {
socket.onopen = (e) => (emitter(actionCreators.socketOpen(e)));
socket.onclose = (e) => (emitter(actionCreators.socketClose(e)));
socket.onerror = (e) => (emitter(actionCreators.socketError(e)));
socket.onmessage = (e) => (emitter(actionCreators.socketMessage(e)));
return () => {
socket.close();
};
});
}
function* internalListener(socket) {
while (true) {
const data = yield take(types.SOCKET_SEND);
socket.send(data.payload);
}
}
function* externalListener(socketChannel) {
while (true) {
const action = yield take(socketChannel);
pendingTasks = [...pendingTasks, action];
}
}
function* wsHandling(action) {
const socket = action.payload.socket;
while (true) {
const socketChannel = yield call(watchMessages, socket);
const { cancel } = yield race({
task: [call(externalListener, socketChannel), call(internalListener, socket)],
cancel: take(types.SOCKET_CLOSE),
});
if (cancel) {
socketChannel.close();
}
}
}
function* tasksScheduler() {
while (true) {
const canDisplayTask = activeTasks.length < 1 && pendingTasks.length > 0;
if (canDisplayTask) {
const [firstTask, ...remainingTasks] = pendingTasks;
pendingTasks = remainingTasks;
yield fork(displayTask, firstTask);
yield call(delay, 300);
}
else {
yield call(delay, 50);
}
}
}
function* displayTask(task) {
activeTasks = [...activeTasks, task];
yield put(task);
yield call(delay, 3000);
activeTasks = _.without(activeTasks, task);
}
export default function* rootSaga(action) {
yield [
takeEvery(types.SOCKET_CONNECT, wsHandling),
takeEvery(types.SOCKET_CONNECT, tasksScheduler),
];
}

How can I override jasmine's buildExpectationResult in order to modify message() function?

I am using protractor for my e2e tests and jasmine2 as framework. I am using a plugin for html reporter with screenshots ( html-report for protractor ).
In these reports there will be shown a list of all failed/passed expects. When the expect fails I get a descriptive message of the expectation. However when the expect passes I only see the word: Passed. The reason behind that is that jasmine overrides the message when the expect passes.
That is done in the following file:
node_modules/protractor/node_modules/jasmine/node_modules/jasmine-core/lib/jasmine-core/jasmine.js
getJasmineRequireObj().buildExpectationResult = function () {
function buildExpectationResult(options) {
var messageFormatter = options.messageFormatter || function () {
},
stackFormatter = options.stackFormatter || function () {
};
var result = {
matcherName: options.matcherName,
message: message(),
stack: stack(),
passed: options.passed
};
if (!result.passed) {
result.expected = options.expected;
result.actual = options.actual;
}
return result;
function message() {
if (options.passed) {
// Here is the message overriden
return 'Passed.';
} else if (options.message) {
return options.message;
} else if (options.error) {
return messageFormatter(options.error);
}
return '';
}
function stack() {
if (options.passed) {
return '';
}
var error = options.error;
if (!error) {
try {
throw new Error(message());
} catch (e) {
error = e;
}
}
return stackFormatter(error);
}
}
return buildExpectationResult;
};
What I wanted is to override this function in my protractor protractor.conf.js file. And replace it with one with the desired behaviour.
I've tried to do so unsuccessfully doing the following:
onPrepare: function () {
jasmine.buildExpectationResult = function () {
function buildExpectationResult(options) {
var messageFormatter = options.messageFormatter || function () {
},
stackFormatter = options.stackFormatter || function () {
};
return {
matcherName: options.matcherName,
expected: options.expected,
actual: options.actual,
message: message(),
stack: stack(),
passed: options.passed
};
function message() {
if (options.message) {
return options.message;
} else if (options.error) {
return messageFormatter(options.error);
}
return "";
}
function stack() {
if (options.passed) {
return "";
}
var error = options.error;
if (!error) {
try {
throw new Error(message());
} catch (e) {
error = e;
}
}
return stackFormatter(error);
}
}
return buildExpectationResult;
};
}
Then my questions is: What is the right way to override a jasmine method?
Since we use gulp task to run protractor tests, we override the lib (like jasmine lib) as one of the gulp task with custom copy. We do that as part of installation or every test execution.
I didn't find any good way to override it unless we create another npm module.
I had the same issue, I'm not sure if my solution
onPrepare: function () {
// ...
jasmine.Spec.prototype.addExpectationResult = function(passed, data, isError) {
var buildExpectationResult = function(options) {
var messageFormatter = options.messageFormatter || function() {},
stackFormatter = options.stackFormatter || function() {};
var result = {
matcherName: options.matcherName,
message: message(),
stack: stack(),
passed: options.passed
};
if(!result.passed) {
result.expected = options.expected;
result.actual = options.actual;
}
return result;
function message() {
if (options.passed) {
return options.message ? options.message : 'Passed';
} else if (options.message) {
return options.message;
} else if (options.error) {
return messageFormatter(options.error);
}
return '';
}
function stack() {
if (options.passed) {
return '';
}
var error = options.error;
if (!error) {
try {
throw new Error(message());
} catch (e) {
error = e;
}
}
return stackFormatter(error);
}
}
var exceptionFormatter = jasmine.ExceptionFormatter;
var expectationResultFactory = function(attrs) {
attrs.messageFormatter = exceptionFormatter.message;
attrs.stackFormatter = exceptionFormatter.stack;
return buildExpectationResult(attrs);
}
var expectationResult = expectationResultFactory(data);
if (passed) {
this.result.passedExpectations.push(expectationResult);
} else {
this.result.failedExpectations.push(expectationResult);
if (this.throwOnExpectationFailure && !isError) {
throw new j$.errors.ExpectationFailed();
}
}
};
// ...
}

Resources