Error when starting websocket application - websocket

I am trying to run the cowboy web socket example but I keep running into an error:
{error,
{bad_return,
{{erws_app,start,[normal,[]]},
{'EXIT',
{undef,
[{websocket_sup,start_link,[],[]},
{application_master,start_it_old,4,
[{file,"application_master.erl"},{line,269}]}]}}}}}
The erlang command I am using to start the application: erl -pa ebin/ -pa deps/*/ebin
then I start the required dependency applications using application:start(app).
I use rebar to compile the application
The code I am using:
erws_app.erl
-module(erws_app).
-behaviour(application).
%% Application callbacks
-export([start/2, stop/1, start_link/2]).
start(_Type, _Args) ->
Dispatch = cowboy_router:compile([
{'_', [
{"/", cowboy_static, {priv_file, websocket, "index.html"}},
{"/websocket", ws_handler, []},
{"/static/[...]", cowboy_static, {priv_dir, websocket, "static"}}
]}
]),
{ok, _} = cowboy:start_http(http, 100, [{port, 8080}],
[{env, [{dispatch, Dispatch}]}]),
websocket_sup:start_link().
start_link(_Type, _Args) ->
Dispatch = cowboy_router:compile([
{'_', [
{"/", cowboy_static, {priv_file, websocket, "index.html"}},
{"/websocket", ws_handler, []},
{"/static/[...]", cowboy_static, {priv_dir, websocket, "static"}}
]}
]),
{ok, _} = cowboy:start_http(http, 100, [{port, 8080}],
[{env, [{dispatch, Dispatch}]}]),
websocket_sup:start_link().
stop(_State) ->
ok.
erws_handler.erl
-module(erws_handler).
-export([init/3]).
-export([websocket_init/3]).
-export([websocket_handle/3]).
-export([websocket_info/3]).
-export([websocket_terminate/3]).
init({tcp, http}, _Req, _Opts) ->
{upgrade, protocol, cowboy_websocket}.
websocket_init(_TransportName, Req, _Opts) ->
erlang:start_timer(1000, self(), <<"Hello!">>),
{ok, Req, undefined_state}.
websocket_handle({text, Msg}, Req, State) ->
{reply, {text, << "That's what she said! ", Msg/binary >>}, Req, State};
websocket_handle(_Data, Req, State) ->
{ok, Req, State}.
websocket_info({timeout, _Ref, Msg}, Req, State) ->
erlang:start_timer(1000, self(), <<"How' you doin'?">>),
{reply, {text, Msg}, Req, State};
websocket_info(_Info, Req, State) ->
{ok, Req, State}.
websocket_terminate(_Reason, _Req, _State) ->
ok.
erws_sup.erl
-module(erws_sup).
-behaviour(supervisor).
%% API.
-export([start_link/0]).
%% supervisor.
-export([init/1]).
%% API.
-spec start_link() -> {ok, pid()}.
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
%% supervisor.
init([]) ->
Procs = [],
{ok, {{one_for_one, 10, 10}, Procs}}.
erws.app.src
{application, erws, [
{description, ""},
{vsn, "1"},
{registered, []},
{applications, [kernel,stdlib,crypto,cowboy,compiler,lager,syntax_tools]},
{mod, { erws_app, []}},
{env, []}
]}.
rebar.config
{erl_opts, [{parse_transform, lager_transform}]}.
{lib_dirs,["deps"]}.
{sub_dirs, ["rel"]}.
{deps, [
{'lager', ".*", {
git, "https://github.com/basho/lager.git", "2.0.2"}
},
{'ranch', ".*", {
git, "https://github.com/extend/ranch.git", "0.9.0"}
},
{'cowlib', ".*", {
git, "https://github.com/extend/cowlib.git", "0.4.0"}
},
{'cowboy', ".*", {
git, "https://github.com/extend/cowboy.git", "0.9.0"}
}
]}.
Help to solve this error is most appreciated.
regards

The error message shows that the function erws_app:start/2 tries to call the function websocket_sup:start_link/0 but no such function exists.
You've named the supervisor module as erws_sup, so replace all websocket_sup:start_link() in erws_app.erl with erws_sup:start_link().

Related

how to render an object with two flux fields without blocking?

I want to render an object composed of two mono or flux elements (below a code snippet):
Mono<List<NodeDTO>> nodeDTOFlux = this.webClient
.get()
.uri(NODES_WITH_LIMIT + limit)
.retrieve()
.onStatus(HttpStatus::isError,
response -> response.bodyToMono(String.class).flatMap(
msg -> Mono.error(new ApiCallException(msg, response.statusCode())))
)
.bodyToFlux(new ParameterizedTypeReference<Node>() {
}).map(node -> nodeMapper.toNodeDTO(node))
.collectList();
Mono<List<EdgeDTO>> edgeDTOFlux = this.webClient
.get()
.uri(EDGES_WITH_LIMIT + limit)
.retrieve()
.onStatus(HttpStatus::isError,
response -> response.bodyToMono(String.class).flatMap(
msg -> Mono.error(new ApiCallException(msg, response.statusCode())))
)
.bodyToFlux(new ParameterizedTypeReference<Edge>() {
}).map(edge -> edgeMapper.toEdgeDTO(edge))
.collectList();
I tried with zip() method but it's not what I aim to do
I tried to return an object like this
GraphDataDTO graphDataDTO = new GraphDataDTO();
graphDataDTO.setEdgeDTOS(edgeDTOFlux);
graphDataDTO.setNodeDTOS(nodeDTOFlux);
I have a result in my console but the object returned
{
"nodeDTOS": {
"scanAvailable": true
},
"edgeDTOS": {
"scanAvailable": true
}
}
the return is done before getting all the flux.. is there any solution without blocking !
thanks in advance.
This should work:
return Mono.zip(nodeDTOFlux, edgeDTOFlux)
.map(tuple2 -> GraphDataDTO.builder().nodeDTO(tuple2.getT1()).edgeDTO(tuple2.getT2()).build())
It creates a Tuple of NodeDTO and EdgeDTO and maps it into GraphDataDTO.

Rust Actix Actor send message to actor

How to send a message to another actor?
pub struct MyWs {
}
impl Actor for MyWs {
type Context = ws::WebsocketContext<Self>;
}
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWs {
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
match msg {
Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),
Ok(ws::Message::Text(message)) => {
//considering that here he sent the message to self
ctx.text(message);
//how to do something like this
//find the actor by index (or uuid) and send text
//actors[0].text(message);
//
},
Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
Ok(ws::Message::Close(reason)) => ctx.close(reason),
_ => (),
}
}
}
#[get("/ws")]
pub async fn websocket(req: HttpRequest, stream: web::Payload,) -> actix_web::Result<HttpResponse> {
let resp = ws::start(
MyWs {},
&req,
stream,
);
return resp;
}
Could I make a hashMap of actors?
pub struct MyWs { sessions: HashMap<Uuid, Socket> }
and later
self.sessions.text(message)
I'm new to rust and I don't see a way to save the socket (the context or actor) to find it and send it messages.
You might want to check the official example of a chat room app using actix web.
https://github.com/actix/examples/blob/743af0ff1a9be6fb1cc13e6583108463c89ded4d/websockets/chat/src/main.rs
There are three key points:
create another server actor and get the address of it.
let server = server::ChatServer::new(app_state.clone()).start();
set the server actor's address as application data of HttpServer.
HttpServer::new(move || {
App::new()
.data(app_state.clone())
// copy server actor's address into app
.data(server.clone())
.service(web::resource("/").route(web::get().to(|| {
HttpResponse::Found()
.header("LOCATION", "/static/websocket.html")
.finish()
})))
.route("/count/", web::get().to(get_count))
// websocket
.service(web::resource("/ws/").to(chat_route))
// static resources
.service(fs::Files::new("/static/", "static/"))
})
.bind("127.0.0.1:8080")?
.run()
.await
store the address while starting websocket actor in function chat_route. And then you could use the address to send messages whenever you want
/// Entry point for our websocket route
async fn chat_route(
req: HttpRequest,
stream: web::Payload,
srv: web::Data<Addr<server::ChatServer>>,
) -> Result<HttpResponse, Error> {
ws::start(
WsChatSession {
id: 0,
hb: Instant::now(),
room: "Main".to_owned(),
name: None,
addr: srv.get_ref().clone(),
},
&req,
stream,
)
}

Spring Webclient retry and execute a code if all retries are exhausted

I have a webhook service that sends events to different sources (URLs). By design, the request timeout is 10s, if it fails, retries to send 3 times. In case, all retries are failed, a code must be executed to disable that URL in DB.
So far, I managed to retry and with delay of 5 seconds. But, I'm not sure how to execute code after failure.
try{
String body = objectMapper.writeValueAsString(webhookDTO);
webClient.post()
.uri(webhook.getUrl())
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(body)
.exchange()
.timeout(Duration.ofSeconds(5))
.retryWhen(Retry.backoff(3, Duration.ofSeconds(5))
.jitter(0d)
.doAfterRetry(retrySignal -> {
logger.info("Retried " + retrySignal.totalRetries());
})
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal)
-> new WebhookTimeoutException()))
.doOnSuccess(clientResponse -> {
logger.info("Event is received by " + client);
})
.subscribe();
} catch (Exception e) {
logger.error("Error on webhook dispatcher: ", e);
}
Can anyone give some examples of how to do this?
You are almost there! Just use doOnError as shown here. The idea here, once after all the attempts failed, you throw WebhookTimeoutException. The doOnError is called only when the error is thrown & updates the DB. The exception class is optional. You can ignore that.
webClient.post()
.uri(webhook.getUrl())
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(body)
.exchange()
.timeout(Duration.ofSeconds(5))
.retryWhen(Retry.backoff(3, Duration.ofSeconds(5))
.jitter(0d)
.doAfterRetry(retrySignal -> {
logger.info("Retried " + retrySignal.totalRetries());
})
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal)
-> new WebhookTimeoutException()))
.doOnSuccess(clientResponse -> {
logger.info("Event is received by " + client);
})
.doOnError(WebhookTimeoutException.class, (msg) -> {
System.out.println("Message :: " + msg);
// here update the DB
dbRepository.save(...);
})
.subscribe();

CompletableFuture List wait for all future issue

I have the below list of CompletableFuture pipeline
for (Integer unit : getIds()) {
futureList.add(CompletableFuture.completedFuture(unit)
.thenApply(id -> CompletableFuture
.completedFuture(service.get1(id))
.thenCombineAsync(CompletableFuture.completedFuture(b), (a, df1) ->
CompletableFuture.completedFuture(service.validate(Optional.ofNullable(a),c,
b, dealerCode))
.thenApplyAsync(status -> CompletableFuture.completedFuture(b))
.thenCombineAsync(CompletableFuture.completedFuture(a), (b1, a1) ->
service.mapToTrans(Optional.ofNullable(a), b, c))
.handle((response, exception) -> {
if(exception == null) {
return response;
}
else {
handler.handleException(exception, results);
return null;
}
})
.thenApplyAsync(t -> service.submitRequest(Optional.ofNullable(a), c))
.thenApplyAsync((t) -> service.saveTransaction(Optional.ofNullable(t)))
.thenAccept(result1 -> results.add(result1))
)
.handle((response, exception) -> handleStage(response, exception, results))
)
.handle((response, exception) -> handleStage(response, exception, results))
);
}
The results array holds the result of all the futures. In the main method this is how I am waiting for all the futures to get completed
CompletableFuture<Void> allFutures = CompletableFuture
.allOf(futureList.toArray(new CompletableFuture[futureList.size()]));
CompletableFuture<List<Object>> allFutureExec = allFutures.thenApply(v -> {
return futureList.stream().map(pg-> pg.join()).collect(Collectors.toList());
});
allFutureExec.get();
The "results" list is supposed to contain the result of all future chain But I can see that main thread is not waiting for all the stages to get executed. The result list has the result of only few futures, though I can see from the logs that all the futures are getting executed. Please let me know if this is the correct way to wait for all the futures to get completed.
UPDATE-1
for (Integer unit : getIds()) {
futureList.add(CompletableFuture
.completedFuture(service.get1(unit))
.thenCombine(CompletableFuture.completedFuture(td), (vd, dealerInfo1) ->
CompletableFuture.completedFuture(service.validate(Optional.ofNullable(vd),sd,
td))
.thenApply(status -> CompletableFuture.completedFuture(service.mapToTrans
(Optional.ofNullable(vd), td,trade, userCredential))
.thenApplyAsync(transaction -> CompletableFuture.completedFuture(service.submit(Optional.ofNullable(vd),transaction))
.thenApply((transaction1) -> service.saveTransaction(Optional.ofNullable(transaction1)))
.thenApply(result1 -> results.add(result1)))))
);
}

Java Stream Collectors.toMap- How to convert to a different concurrent Map [duplicate]

How can I Use Collectors to collect in a ConcurrentHashMap instread of putting manually into ConcurrentHashMap
ConcurrentHashMap<String, String> configurationMap = new ConcurrentHashMap<>();
List<Result> results = result.getResults();
results.stream().forEach(res -> {
res.getSeries().stream().forEach(series -> {
series.getValues().stream().forEach(vals ->{
configurationMap.put(vals.get(1).toString(),vals.get(2).toString());
});
});
});
//Note: vals is List<List<Object>> type
Help will be appreciated.
You can use Collectors.toConcurrentMap
results.stream()
.flatMap(res -> res.getSeries().stream())
.flatMap(series -> series.getValues().stream())
.collect(Collectors.toConcurrentMap(
vals -> vals.get(1).toString(),
vals -> vals.get(2).toString()));
We can do this as follows also:
results.stream()
.flatMap(res -> res.getSeries().stream())
.flatMap(series -> series.getValues().stream())
.collect(Collectors.toMap(
vals -> vals.get(1).toString(),
vals -> vals.get(2).toString(),
(vals1,vals2) -> vals2,
ConcurrentHashMap::new);

Resources