Flux.create() not generating events - spring

I'm trying to use Flux to generate asynchronous server sent events using Flux.create. When my client connects the request eventually times out with no event ever received. I hard-coded in an event to be sent by the Flux.create just to see data flow, but still nothing received client side.
#GetMapping(path = "/stream", headers = "Accept=*/*", consumes = MediaType.ALL_VALUE, produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<PricingDTO>> getEventStream() {
final Flux<ServerSentEvent<PricingDTO>> flux = Flux.<ServerSentEvent<PricingDTO>>create(emitter -> {
final PricingDTO pricing = new PricingDTO();
pricing.setId(99L);
emitter.next(ServerSentEvent.builder(pricing).build());
});
return flux;
}
Client side (Angular) code:
const eventSource = new EventSource(url);
eventSource.onmessage = (event) => {
console.debug('Received event: ' + event);
const json = JSON.parse(event.data);
// Should be PricingDTO record here
};
eventSource.onerror = (error) => {
if (eventSource.readyState === EventSource.CLOSED) {
console.log('The stream has been closed by the server.');
eventSource.close();
} else {
console.log('Error here: ' + error);
}
};
I never see an event come through the EventSource. Eventually the request times out and I see the error: net::ERR_EMPTY_RESPONSE
I'm new to using WebFlux and I suspect I'm missing some initialization on the FluxStream before I return the Flux result. I have debugged and do see the request being received by my web service and the Flux object being returned. Any idea why I'm not receiving my events?

Your webflux code seems fine. I tested this with the following simplified example (without your custom classes).
#SpringBootApplication
#RestController
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#GetMapping(path = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> getEventStream() {
return Flux.create(emitter -> emitter.next("hi").next("hi2"));
}
}
When connecting to the steam in chrome you get to see the events coming in just fine:
data:hi
data:hi2
the problem either lies in your accept header filter, or on the client side. You could ofcourse validate this by connecting to your stream in a browser (or better, a test)

Related

Masstransit: GetSendEndpoint

I have a producer, which send more than 1000 messages in a minute to a specific endpoint. I’m using Microsoft DI and I’ve configured the send Endpoint as described here https://masstransit-project.com/usage/producers.html#send .
// Masstransit setup
serviceCollection.AddMassTransit(mt =>
{
mt.UsingAzureServiceBus((ctx, cfg) =>
{
cfg.Host(massTransitSettings.TestServiceBusConnectionString);
cfg.ReceiveEndpoint("mytestmessage", e =>
{
e.MaxDeliveryCount = 3; //How many times the transport will redeliver the message on negative acknowledgment
});
});
});
serviceCollection.AddTransient<ITestMessageProducer, TestMessageProducer>();
// Producer setup
public class TestMessageProducer : ITestMessageProducer
{
private readonly ISendEndpointProvider _testEndpoint;
public TestMessageProducer(ISendEndpointProvider testEndpoint)
{
_testEndpoint = testEndpoint;
}
public async Task SendTestMessage(ITestMessage testmessage)
{
var endpoint = await _testEndpoint.GetSendEndpoint(new Uri("queue:mytestmessage"));
await endpoint.Send(testmessage);
}
}
Query:
The SendTestMessage function has been called very frequently as mention above. Will it be ok to call “GetSendEndpoint” everytime? I have read somewhere that GetSendEndpoint creates a new instance of ISendEndpoint everytime.
Will the MaxDeliveryCount still be worked on my sendendpoint?
Thank you.
Send endpoints are cached by address, only a single instance will be created.
MaxDeliveryCount is a receive endpoint concern, but you should not configure a receive endpoint without consumers as all messages will be moved to the _skipped queue.

Spring Boot RSocket send a message within a Message Mapping

Staring with the tutorial code at benwilcock/spring-rsocket-demo I am trying to write a server that replicates messages to a second server before responding to a client.
To try to debug my issues I am only attempting a trivial ping-pong exchange between servers. Only when the second server responds to the pong message should the first server reply to the client:
#MessageMapping("request-response")
Mono<Message> requestResponse(final Message request) {
// register a mono that will be completed when replication to another server has happened
String uuid = UUID.randomUUID().toString();
Mono<Message> deferred = Mono.create(sink -> replicationNexus.registerRequest(uuid, sink));
// FIXME attempt to send a nested request-response message that will complete the outer message later
this.requesterMono.flatMap(requester -> requester.route("pong")
.data(uuid)
.retrieveMono(String.class))
.subscribeOn(Schedulers.elastic())
.subscribe( uuid2 -> replicationNexus.complete(uuid2, new Message(SERVER, RESPONSE)));
// return the deferred work that will be completed by the pong response
return deferred;
}
That logic is trying to use this answer to create a connection to the second server that will reconnect:
this.requesterMono = builder.rsocketConnector(connector -> connector
.reconnect(Retry.fixedDelay(Integer.MAX_VALUE, Duration.ofSeconds(1))))
.connectTcp("localhost", otherPort).cache();
To complete the picture here is the trivial ping-pong logic:
#MessageMapping("pong")
public Mono<String> pong(String m) {
return Mono.just(m);
}
and here is the logic that holds the state of the outer client response that is completed when the other server responds:
public class ReplicationNexus<T> {
final Map<String, MonoSink<T>> requests = new ConcurrentHashMap<>();
public void registerRequest(String v, MonoSink<T> sink) {
requests.put(v, sink);
}
public boolean complete(String uuid, T message) {
Optional<MonoSink<T>> sink = Optional.of(requests.get(uuid));
if( sink.isPresent() ){
sink.get().success(message);
}
return sink.isPresent();
}
}
Debugging the second server it never runs the pong method. It seems that the first server does not actually send the inner request message.
What is the correct way to run an inner request-response exchange that completes an outer message exchange with automated reconnection logic?
Not sure if I'm missing some of the complexity of your question, but if the middle server is just activing like a proxy I'd start with the simplest case of chaining through the calls. I feel like I'm missing some nuance of the question, so let's work through that next.
#MessageMapping("runCommand")
suspend fun runCommandX(
request: CommandRequest,
): Mono<String> {
val uuid = UUID.randomUUID().toString()
return requesterMono
.flatMap { requester: RSocketRequester ->
requester.route("pong")
.data("TEST")
.retrieveMono(String::class.java)
}
.doOnSubscribe {
// register request with uuid
}
.doOnSuccess {
// register completion
}
.doOnError {
// register failure
}
}
Generally if you can avoid calling subscribe yourself in typical spring/reactive/rsocket code. You want the framework to do this for you.

How to upload just an image using Retrofit 2.0

Trying to upload an image and it keeps sending as just bytes, not an image file. This is a very simple call, I don't need to send any params other than the image itself. I don't know how to format logs so I won't post the error here unless requested to.
The service:
public interface FileUploadService {
#Multipart
#POST("upload_profile_picture")
Call<ResponseBody> uploadProfilePicture(#Part("profile_picture") RequestBody file);
}
The call being made (a file is generated earlier, had to remove this code because SO needs the post to be mainly words..dumb..):
// Generate the service from interface
FileUploadService service = ServiceGenerator.createService(FileUploadService.class, this);
// Create RequestBody instance from file
RequestBody requestFile =
RequestBody.create(MediaType.parse("image/*"), imageFile);
Log.d(LOG_TAG, "formed file");
// finally, execute the request
Call<ResponseBody> call = service.uploadProfilePicture(requestFile);
Log.d(LOG_TAG, "sending call");
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call,
Response<ResponseBody> response) {
Log.d(LOG_TAG, "success");
Log.d(LOG_TAG, response.toString());
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.d(LOG_TAG, "failure");
Log.e(LOG_TAG, t.getMessage());
}
});
Is the issue with the MediaType.parse method? I've tried "multipart/form-data", "image/jpeg", and the above as well and nothing has worked.
The server team has said they are receiving the call, just as bytes and no image file.
I keep getting a 400 because it's sending all bytes. How can I just send this? Do I need to send as a multipart or what? From what I've seen, you just need to tag the param in the method with #Body and do the above and it should all work. Can anybody tell me why this is happening? Thanks!
This is a known issue in Retrofit 2.
Edit: Support for OkHttp's MultipartBody.Part has been added in the final 2.0 release.
In order to get it working, you need to change your interface a little bit first:
#Multipart
#POST("upload_profile_picture")
Call<ResponseBody> uploadProfilePicture(#Part MultipartBody.Part file);
Then you have to create the Part and make the call like this:
MultipartBody.Part file = MultipartBody.Part.createFormData(
"file",
imageFile.getName(),
RequestBody.create(MediaType.parse("image/*"), imageFile));
Call<ResponseBody> call = service.uploadProfilePicture(file);

Windows Phone sends more than one web requests in order in a call

Reccently, I am working on a project in Windows Phone. and In this project, to validate a user, I need to check at 3 web API, the logic is like below:
Step 1: access web api 1 to get the token
Step 2: access web api 2 to get the username/password by the token retrieved in Step 1
Step 3: access web API 3 to validate the user name/password in step 2
you can see we need to access those 3 API in order. as well know, window phone now access the network asynchronously, which causes a big challenge on make those API access in order, and which make the soure code hard to maintainace.
I also consider the synchronous source code like below, but I found there are some problems to access the network,many exeption will be thrown. For example, when an exception is thrown, I try to use asynchronous web request to access the same URL, it is OK. I am strugglig in it now. And I have to introduce thread to call it to avoid to block the UI thread.
internal static class HttpWebRequestExtensions
{
public const int DefaultRequestTimeout = 60000;
public static bool IsHttpExceptionFound = false;
public static WebResponse GetResponse(this WebRequest request, int nTimeOut = DefaultRequestTimeout)
{
var dataReady = new AutoResetEvent(false);
HttpWebResponse response = null;
var callback = new AsyncCallback(delegate(IAsyncResult asynchronousResult)
{
try
{
response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
dataReady.Set();
}
catch(Exception e)
{
IsHttpExceptionFound = true;
}
});
request.BeginGetResponse(callback, request);
if (dataReady.WaitOne(nTimeOut))
{
return response;
}
return null;
}
public static WebResponse PostRequest(this HttpWebRequest request, String postData, int nTimeOut = DefaultRequestTimeout)
{
var dataReady = new AutoResetEvent(false);
HttpWebResponse response = null;
var callback = new AsyncCallback(delegate(IAsyncResult asynchronousResult)
{
Stream postStream = request.EndGetRequestStream(asynchronousResult); //End the operation.
byte[] byteArray = Encoding.UTF8.GetBytes(postData); //Convert the string into a byte array.
postStream.Write(byteArray, 0, postData.Length); //Write to the request stream.
postStream.Close();
dataReady.Set();
});
request.BeginGetRequestStream(callback, request);
if (dataReady.WaitOne(nTimeOut))
{
response = (HttpWebResponse)request.GetResponse(nTimeOut);
if (IsHttpExceptionFound)
{
throw new HttpResponseException("Failed to get http response");
}
return response;
}
return null;
}
}
Any suggestion on using asynchronous web request to solve my case?
There's an example here of using asynchronous web services in a chained manner to call the Microsoft Translator service on WP7
Maybe it will give you some pointers?
http://blogs.msdn.com/b/translation/p/wp7translate.aspx

SignalR not broadcasting

I'm trying to create a server to client broadcast mechanism with SignalR and it doesnt seem to do anything.
I have a hub like this:
public class DataMessageService : Hub, IClientNotificationService
{
dynamic _clients;
public DataMessageService(IConnectionManager connectionManager)
{
_clients = connectionManager.GetClients<DataMessageService>();
}
public void SendDataChangeNotification(string entityName, string changeType, string id)
{
_clients.dataChangeNotification(new string[] {entityName, changeType, id});
}
}
My _Layouts.cshtml has this:
var _centralHub;
$(function() {
// startup the signalr hub and get the proxies we need
_centralHub = $.connection.dataMessageService;
$.connection.hub.start();
});
And I have some code in a partial which is loaded by a jquery tab using ajax:
_centralHub.dataChangeNotification = function (data) {
alert(data[0] + "::" + data[1] + "::" + data[2]);
if (data[0] == 'User') {
grid.refresh();
}
};
Now in the data layer, when some crud action occurs, I call DataMessageService.SendDataChangeNotification but nothing happens at the client end.
Am I missing something?
Update: I thought it might be something to do with the vs web server thingy but it also fails when using full IIS (on Win 7).
Another Update:
I had confused my service with my hub. I'v'e now split these so it looks like the following, but it still doesnt work.
public class DataMessageService : IClientNotificationService
{
public void SendDataChangeNotification(string entityName, string changeType, string id)
{
IConnectionManager icm = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
dynamic clients = icm.GetClients<DataMessageHub>();
clients.dataChangeNotification(new string[] { entityName, changeType, id });
}
}
public class DataMessageHub : Hub
{
}
:(
Even more info:
This works with FireFox but not with IE or Chrome.
I also tried to create a simple sample app and this worked fine with Chrome and IE.
Given that we don't have web sockets available to us, long polling may not be a good idea for our users/infrastructure. Maybe one day...
A new instance of the hub is created every time it is resolved so you can't persist state like that.
You can get all clients in the hub from this.Clients.
To broadcast from outside of the hub class use this:
IConnectionManager connectionManager = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
dynamic clients = connectionManager.GetClients<DataMessageService>();
clients.dataChangeNotification(new string[] {entityName, changeType, id});

Resources