I am using Play framework 2.1.
I am work with web-socket and I need to find way to close them from server.
is there way do do it ?
Example from docs Handling WebSockets:
Let’s write another example that totally discards the input data and closes the socket just after sending the Hello! message:
public static WebSocket<String> index() {
return new WebSocket<String>() {
public void onReady(WebSocket.In<String> in, WebSocket.Out<String> out) {
out.write("Hello!");
out.close()
}
}
}
ScalaWebSockets:
def index = WebSocket.using[String] { request =>
// Just consume and ignore the input
val in = Iteratee.consume[String]()
// Send a single 'Hello!' message and close
val out = Enumerator("Hello!") >>> Enumerator.eof
(in, out)
}
Related
In the calculator example, I have added a new interface in the capnp file like below:
interface Main {
getCalculator #0 () -> (calculator :Calculator);
}
My objective is to make use of multiple interfaces through one implementation Main. How can I achieve this? The extra client code is below (when I try to use calculator it throws a null capability exception):
capnp::EzRpcClient client1(argv[1]);
Main::Client main = client1.getMain<Main>();
auto& waitScope1 = client1.getWaitScope();
auto request1 = main.getCalculatorRequest();
auto evalPromise1 = request1.send();
auto response1 = evalPromise1.wait(waitScope1);
auto calculator = response1.getCalculator();
auto request = calculator.evaluateRequest();
request.getExpression().setLiteral(123);
// Send it, which returns a promise for the result (without blocking).
auto evalPromise = request.send();
// Using the promise, create a pipelined request to call read() on the
// returned object, and then send that.
auto readPromise = evalPromise.getValue().readRequest().send();
// Now that we've sent all the requests, wait for the response. Until this
// point, we haven't waited at all!
auto response = readPromise.wait(waitScope1);
KJ_ASSERT(response.getValue() == 123);
Changes to the server's main implementation file are given below:
class MainImpl : public Main::Server {
public:
kj::Promise<void> getCalculator(GetCalculatorContext context) override {
context.getResults();
return kj::READY_NOW;
}
};
The main function in server serves MainImpl. I do get the calculator object but how can I further invoke methods on that object?
Your getCalculator() function on the server side does not do anything right now. You need to actually set the result if you want the Calculator to get back to the client. That's why you get your "null capability exception".
Something like this:
class MainImpl : public Main::Server {
public:
kj::Promise<void> getCalculator(GetCalculatorContext context) override {
context.getResults().setCalculator(<the calculator capability>);
return kj::READY_NOW;
}
};
That will require you to create the Calculator client on the server side (i.e. you won't do Calculator::Client calculator = client.getMain<Calculator>(); on the client side anymore, since calculator will come from getCalculator()).
Could I response to someone who send his first DM to me in JDA?
This question will depend on what you need, you can use the
public void onPrivateMessageReceived(#Nonnull PrivateMessageReceivedEvent event) {
//code
}
method to get the private channel for DM's.
After that, you could check the history with the user. This would be example code, so you need to edit it to your needs:
#Override
public void onPrivateMessageReceived(#Nonnull PrivateMessageReceivedEvent event) {
if (event.getUser().isBot()) return; // Don't do anything if the user is bot (aka, self)
if (event.getChannel().getHistory().size() < 2) { //Made it <2 in case array starts with 0
event.getChannel().sendMessage("HI THIS IS THE MESSAGE I WANT TO SEND").queue();
}
}
I'm implementing a server using Ktor, and one of the calls is to get a (potentially large) list of users that will come from a database query. I would like to chunk my response, so my user call looks like this:
fun users() =
flow {
emit("A")
emit("B")
emit("C")
}
Where the emit calls will eventually be replaced with data from a result set. The route looks like this:
route("Users") {
get() {
call.respondTextWriter {
users().collect { write(it) }
flush()
}
}
// other calls, e.g. post(), delete(), etc.
However, both calls to write and flush show a warning: "Inappropriate blocking method call". What am I missing?
Thanks.
Eh, a little digging provided the answer:
private suspend fun <T> flowResponse(writer: Writer, flow: Flow<T>) {
val gson = Gson()
flow.collect { data ->
withContext(Dispatchers.IO) {
writer.write(gson.toJson(data))
}
}
withContext(Dispatchers.IO) { writer.flush() }
}
And then:
get() {
call.respondTextWriter {
flowResponse(this, Users.all())
}
}
Slight change from the question, as my Users.all() call returns a User object that then gets converted to JSON. Perhaps there is a way to simply stream the objects and let the content negotiator do the translation, but I haven't gotten there yet.
I have problem with correctly saving a file after its download is complete in Project Reactor.
class HttpImageClientDownloader implements ImageClientDownloader {
private final ExchangeFunction exchangeFunction;
HttpImageClientDownloader() {
this.exchangeFunction = ExchangeFunctions.create(new ReactorClientHttpConnector());
}
#Override
public Mono<File> downloadImage(String url, Path destination) {
ClientRequest clientRequest = ClientRequest.create(HttpMethod.GET, URI.create(url)).build();
return exchangeFunction.exchange(clientRequest)
.map(clientResponse -> clientResponse.body(BodyExtractors.toDataBuffers()))
//.flatMapMany(clientResponse -> clientResponse.body(BodyExtractors.toDataBuffers()))
.flatMap(dataBuffer -> {
AsynchronousFileChannel fileChannel = createFile(destination);
return DataBufferUtils
.write(dataBuffer, fileChannel, 0)
.publishOn(Schedulers.elastic())
.doOnNext(DataBufferUtils::release)
.then(Mono.just(destination.toFile()));
});
}
private AsynchronousFileChannel createFile(Path path) {
try {
return AsynchronousFileChannel.open(path, StandardOpenOption.CREATE);
} catch (Exception e) {
throw new ImageDownloadException("Error while creating file: " + path, e);
}
}
}
So my question is:
Is DataBufferUtils.write(dataBuffer, fileChannel, 0) blocking?
What about when the disk is slow?
And second question about what happens when ImageDownloadException occurs ,
In doOnNext I want to release the given data buffer, is that a good place for this kind operation?
I think also this line:
.map(clientResponse -> clientResponse.body(BodyExtractors.toDataBuffers()))
could be blocking...
Here's another (shorter) way to achieve that:
Flux<DataBuffer> data = this.webClient.get()
.uri("/greeting")
.retrieve()
.bodyToFlux(DataBuffer.class);
Path file = Files.createTempFile("spring", null);
WritableByteChannel channel = Files.newByteChannel(file, StandardOpenOption.WRITE);
Mono<File> result = DataBufferUtils.write(data, channel)
.map(DataBufferUtils::release)
.then(Mono.just(file));
Now DataBufferUtils::write operations are not blocking because they use non-blocking IO with channels. Writing to such channels means it'll write whatever it can to the output buffer (i.e. may write all the DataBuffer or just part of it).
Using Flux::map or Flux::doOnNext is the right place to do that. But you're right, if an error occurs, you're still responsible for releasing the current buffer (and all the remaining ones). There might be something we can improve here in Spring Framework, please keep an eye on SPR-16782.
I don't see how your last sample shows anything blocking: all methods return reactive types and none are doing blocking I/O.
We are working with spring boot 2.0.0.BUILD_SNAPSHOT and spring boot webflux 5.0.0 and currently we cant transfer a flux to a client on request.
Currently I am creating the flux from an iterator:
public Flux<ItemIgnite> getAllFlux() {
Iterator<Cache.Entry<String, ItemIgnite>> iterator = this.getAllIterator();
return Flux.create(flux -> {
while(iterator.hasNext()) {
flux.next(iterator.next().getValue());
}
});
}
And on request I am simply doing:
#RequestMapping(value="/all", method=RequestMethod.GET, produces="application/json")
public Flux<ItemIgnite> getAllFlux() {
return this.provider.getAllFlux();
}
When I now locally call localhost:8080/all after 10 seconds I get a 503 status code. Also as at client when I request /all using the WebClient:
public Flux<ItemIgnite> getAllPoducts(){
WebClient webClient = WebClient.create("http://localhost:8080");
Flux<ItemIgnite> f = webClient.get().uri("/all").accept(MediaType.ALL).exchange().flatMapMany(cr -> cr.bodyToFlux(ItemIgnite.class));
f.subscribe(System.out::println);
return f;
}
Nothing happens. No data is transferred.
When I do the following instead:
public Flux<List<ItemIgnite>> getAllFluxMono() {
return Flux.just(this.getAllList());
}
and
#RequestMapping(value="/allMono", method=RequestMethod.GET, produces="application/json")
public Flux<List<ItemIgnite>> getAllFluxMono() {
return this.provider.getAllFluxMono();
}
It is working. I guess its because all data is already finished loading and just transferred to the client as it usually would transfer data without using a flux.
What do I have to change to get the flux streaming the data to the web client which requests those data?
EDIT
I have data inside an ignite cache. So my getAllIterator is loading the data from the ignite cache:
public Iterator<Cache.Entry<String, ItemIgnite>> getAllIterator() {
return this.igniteCache.iterator();
}
EDIT
adding flux.complete() like #Simon Baslé suggested:
public Flux<ItemIgnite> getAllFlux() {
Iterator<Cache.Entry<String, ItemIgnite>> iterator = this.getAllIterator();
return Flux.create(flux -> {
while(iterator.hasNext()) {
flux.next(iterator.next().getValue());
}
flux.complete(); // see here
});
}
Solves the 503 problem in the browser. But it does not solve the problem with the WebClient. There is still no data transferred.
EDIT 3
using publishOn with Schedulers.parallel():
public Flux<ItemIgnite> getAllFlux() {
Iterator<Cache.Entry<String, ItemIgnite>> iterator = this.getAllIterator();
return Flux.<ItemIgnite>create(flux -> {
while(iterator.hasNext()) {
flux.next(iterator.next().getValue());
}
flux.complete();
}).publishOn(Schedulers.parallel());
}
Does not change the result.
Here I post you what the WebClient receives:
value :[Item ID: null, Product Name: null, Product Group: null]
complete
So it seems like he is getting One item (out of over 35.000) and the values are null and he is finishing after.
One thing that jumps out is that you never call flux.complete() in your create.
But there's actually a factory operator that is tailored to transform an Iterable to a Flux, so you could just do Flux.fromIterable(this)
Edit: in case your Iterator is hiding complexity like a DB request (or any blocking I/O), be advised this spells trouble: anything blocking in a reactive chain, if not isolated on a dedicated execution context using publishOn, has the potential to block not only the entire chain but other reactive processes has well (as threads can and will be used by multiple reactive processes).
Neither create nor fromIterable do anything in particular to protect from blocking sources. I think you are facing that kind of issue, judging from the hang you get with the WebClient.
The problem was my Object ItemIgnite which I transfer. The system Flux seems not to be able to handle this. Because If I change my original code to the following:
public Flux<String> getAllFlux() {
Iterator<Cache.Entry<String, ItemIgnite>> iterator = this.getAllIterator();
return Flux.create(flux -> {
while(iterator.hasNext()) {
flux.next(iterator.next().getValue().toString());
}
});
}
Everything is working fine. Without publishOn and without flux.complete(). Maybe someone has an idea why this is working.