I've seen a number of Atmosphere examples including pub-sub. I want to do something like pub-sub (client subscribes to a channel that is unique to that client; server periodically publishes to that channel), except that the client will send data to the server as well. The client will send data in response to data sent by the server and in other cases when something important happens on the client that the server needs to know about (which the server doesn't need to acknowledge).
Is it even possible to do this with Atmosphere?
It might look something like this:
#Stateless
#Path("/id/{clientId}/key/{clientKey}")
public class MyService {
#POST
#Produces("application/xml")
#Consumes("application/xml")
#Suspend
public StreamingOutput subscribe(#PathParam("clientId") String clientId,
#PathParam("clientKey") String clientKey,
#Context Broadcaster broadcaster,
InputStream body) {
if (!authenticate(clientId, clientKey) {
throw new WebApplicationException(401);
}
broadcaster.setID(clientId);
// Do something here... Not sure what
}
}
But there are a couple of problems here:
The incoming connection will suspend, so it won't be able to send anything to the server except when resumed via broadcast;
Any usage of the InputStream will result in blocking I/O, which kind of defeats the purpose of using Atmosphere.
Both of these problems could be solved simply by removing #Suspend, but then I'm in the thread-per-connection situation.
I get the feeling that Atmosphere isn't going to be the appropriate technology here and perhaps I might have to do something a bit lower level. But I'm not sure how to go about it. Ideas?
Edit:
I can't find a straightforward way of parsing XML asynchronously anyway, so this whole thing is looking less like something that can be done asynchronously.
Just broadcast a Callable to execute your asynchronous XML parsing. Take a look at this sample:
TwitterFeed
Related
We try to publish and subscribe to MQTT protocol using smallrye reactive messaging. We managed to actually publish a message into a specific topic/channel through the following simple code
import io.smallrye.mutiny.Multi;
import org.eclipse.microprofile.reactive.messaging.Outgoing;
import javax.enterprise.context.ApplicationScoped;
import java.time.Duration;
#ApplicationScoped
public class Publish {
#Outgoing("pao")
public Multi<String> generate() {
return Multi.createFrom().ticks().every(Duration.ofSeconds(1))
.map(x -> "A Message in here");
}
}
What we want to do is to call whenever we want the generate() method somehow with a dynamic topic, where the user will define it. That one was our problem but then we found these classes from that repo in github. Package name io.smallrye.reactive.messaging.mqtt
For example we found that there is a class that says it makes a publish call to a MQTT broker(Mosquitto server up).
Here in that statement SendingMqttMessage<String> message = new SendingMqttMessage<String>("myTopic","A message in here",0,false);
We get the a red underline under the SendingMqttMessage<String> saying 'SendingMqttMessage(java.lang.String, java.lang.String, io.netty.handler.codec.mqtt.MqttQoS, boolean)' is not public in 'io.smallrye.reactive.messaging.mqtt.SendingMqttMessage'. Cannot be accessed from outside package
UPDATE(Publish done)
Finally made a Publish request to the mqtt broker(a mosquitto server) and all this with a dynamic topic configured from user. As we found out the previous Class SendingMqttMessage was not supposed to be used at all. And we found out that we also needed and emitter to actually make a publish request with a dynamic topic.
#Inject
#Channel("panatha")
Emitter<String> emitter;
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response createUser(Device device) {
System.out.println("New Publish request: message->"+device.getMessage()+" & topic->"+device.getTopic());
emitter.send(MqttMessage.of(device.getTopic(), device.getMessage()));
return Response.ok().status(Response.Status.CREATED).build();
}
Now we need to find out about making a Subscription to a topic dynamically.
first to sett us to the same page:
Reactive messaging does not work with topics, but with channels.
That is important to note, because you can exclusively read or write to a channel. So if you want to provide both, you need to configure two channels pointing at the same topic, one incoming and one outgoing
To answer your question:
You made a pretty good start with Emitters, but you still lack the dynamic nature you'd like.
In your example, you acquired that Emitter thru CDI.
Now that is all we need, to make this dynamic, since we cann dynamically inject Beans at runtime using CDI like this:
Sending Messages
private Emitter<byte[]> dynamicEmitter(String topic){
return CDI.current().select(new TypeLiteral<Emitter<byte[]>>() {}, new ChannelAnnotation(topic)).get();
}
please also note, that i am creating a Emitter of type byte[], as this is the only currently supportet type of the smallrye-mqtt connector (version 3.4.0) according to its documentation.
Receiving Messages
To read messages from a reactive messaging channel, you can use the counterpart of the Emitter, which is the Publisher.
It can be used analog:
private Publisher<byte[]> dynamicReceiver(String topic){
return CDI.current().select(new TypeLiteral<Publisher<byte[]>>() {}, new ChannelAnnotation(topic)).get();
}
You can then process these Date in any way you like.
As demo, it hung it on a simple REST Endpoint
#GET
#Produces(MediaType.SERVER_SENT_EVENTS)
public Multi<String> stream(#QueryParam("topic") String topic) {
return Multi.createFrom().publisher(dynamicReceiver(topic)).onItem().transform(String::new);
}
#GET
#Path("/publish")
public boolean publish(#QueryParam("msg") String msg, #QueryParam("topic") String topic) {
dynamicEmitter(topic).send(msg.getBytes());
return true;
}
One more Thing
When creating this solution I hit a few pitfalls you should know about:
Quarkus removes any CDI-Beans that are "unused". So if you want to inject them dynamically, you need to exclude those, or turne off that feature.
All channels injected that way must be configured. Otherwise the injection will fail.
For some Reason, (even with removal completely disabled) I was unable to inject Emitters dynamically, unless they are ever injected elsewhere.
I've a scheduler class and another class a custom http client.
The scheduler is initialized on application start up and does the work in background for example querying a service (url) every 30 seconds and write the data to logs.
The http client is created with the url as well.
The url can change anytime so I need to make sure whenever it is the both log scheduler and http client are reinitialized with new url.
public class LogScheduler {
public log() {
synchronized(globallock) {
String url = getUrl();
//log some activity
}
}
}
We have another scheduler which is looking for new url every 30 minutes.
public class UrlScheduler {
private volatile String url;
public void check() {
String url = service.getNewUrl();
if(url!=this.url) {
synchronized(globallock) {
this.url=url;
reinitialize http client
}
}
}
public String getUrl(){
return url;
}
}
Right now I'm using global lock to make sure the log scheduler sees the value as soon it is changed by url scheduler. I really don't like the idea of using global lock as it breaks encapsulation along with other issues.
How could I change my set up to to reinitialize log scheduler and http client as soon the url is changed and sync them as it is changed in order ? I would like to avoid re-initialization if url hasn't changed.
Also how could I block the ui thread using http client if the url is being updated when the request was made.
This is a spring application.
Let me know if it is not clear and I'm happy to provide more details.
Working with limited information, if anything wouldn't work let me know and I'll change it.
To me the simplest thing is to decouple the HTTPClient from anything that may need it. Decoupling the client means you don't need to deal with synchronization issues in classes that are focused on other things(e.g. logging or pinging the service)
Here's a diagram. https://i.imgur.com/PWsXx2G.png
It seems like you'd be changing very little. The main differences is you'd create a wrapper for your HTTPClient, that way in the client you could synchronize it to make sure the HTTPClient is always the correct one.
An example wrapper, don't use this as it's very simple
public class HTTPClientProxy{
private final Object syncLock = new Object();
private HTTPClient client;
public HTTPClient getClient(){
synchronized(syncLock){
return client;
}
}
public void updateClient(URL url){
synchronized(syncLock){
client = new HTTPClient(url);
}
}
}
One potential issue, and one that I'm not sure about. Is if you have multiple services, and they all are bundler (e.g. are linked and need to use the same URL). Then you'll need to have something else on that side, so you can ensure they all use the same client. However this would be a good start as it stops you from worrying about getting bad data from the HTTPClient and moves that functionality into the proxy class.
I believe the observer pattern is useful here.
The URL scheduler, or whatever class is responsible for the knowing the state of the URL at any given point (called the subject), would maintain a list of observers (the other objects which wish to be notified when the URL changes), being the log scheduler and the other http client.
The observers simply implement an interface providing a notification function which accepts the new URL as a parameter. Whenever the url changes, the subject notifies everyone in its list of observers by calling their notification functions.
This makes it so that the log scheduler and other http client are only notified when the URL changes, and they are notified immediately.
If you wished to decouple the observers and subjects (generally more useful when there are many observers observing many subjects), you could build an event manager using a mediator pattern. However, this would probably be overkill given your requirements.
How to implement one-way operation in Web Services (using Java or Spring annotations)?
I have tried to add one way as given below
#WebService
public interface DanduServices {
#Oneway
public void saveDanduInformation(#WebParam(name = "serv") ServDTO Serv, #WebParam(name = "dandu") DanduDTO danduDto);
but it is still request-response not asynchronus or one way.
Could anyone suggest to make a operation one-way in service endpoint and let other operations behave as per request-response?
You need to think in terms of the protocol as well though. In HTTP when you send a request you wait for a response, if no response comes back after an amount of time then you will receive a time-out error. So when you talk about one-way (you should rather say async request maybe) you really need to specify exactly what you mean. Do you want to have confirmation that your message was received i.e. have the server respond back with an OK status code and go off and complete it's task but you not wait for the task to be completed? Then you would need to spawn another thread. Spring has AOP for this the same way it has for transactions with #Transactional. Instead you annotated your method with #Async and return a Future<Something>. You'll also need #EnableAsync in your config. Refer to this article for an example Hot To Do #Async
If you don't even care about if the server received your request you don't want to use TCP/HTTP but instead UDP which is used in VOIP (phone over internet) for instance and is quicker, but it will depend on your client.
I need to show off an example of server sent events. I learned about it in a spring talk. People used Webflux there to show the reactive principles. I understood the part on how this will free thread resources because the request thread won't be blocked until the job is done and the server returns the response.
I have an example here but actually I don't really know how I can make this thread resource example be clear enough.
I do not want to use the WebFlux framework here. Just need to know what to put into a separate thread here - if necessary at all?!
As you can see I have a GetMapping to subscribe to the event stream. And then I have a GetMapping to launch or fire an event. This example is fast for sure but should be considered as heavy database call.
So I actually need to have the whole logic be separated in another thread right? So the request thread is free as soon as possible?
#RestController
public class EventStreamRequestHandler {
#Autowired
ObjectMapper objectMapper;
SseEmitter sseEmitter = new SseEmitter(1000000L);
#GetMapping("/get/event/stream")
public SseEmitter getStream() {
return this.sseEmitter;
}
#GetMapping("/launch/event")
public void fireEvent() throws IOException {
Person peter = new Person("Peter", "25");
String valueAsString = objectMapper.writeValueAsString(peter);
SseEmitter.SseEventBuilder sseEventBuilder = SseEmitter.event()
.id("foo")
.name("awesome-event")
.data(valueAsString);
sseEmitter.send(sseEventBuilder);
}
}
Yes, Server sent events are supposed to send messages to the client asynchronously without client keep on polling for message.
The sending of messages from client to server needs to be done asynchronously. With the way you have done it. When a GET request is sent to /get/event/stream an SseEmitter will be created but messages will only be sent when a GET request is sent to /launch/event. And the GET request thread for /launch/event will be used to send the message.
Sometime back I wrote post to send SSE messages using a different thread. I hope this helps.
But I don't recommend storing the SseEmitter in an instance variable as it will overridden by multiple requests. You must at least make it ThreadLocal
I am using CometD and I have a service setup (Java on the server side) as follows:
http://localhost:8086/service/myService/get-player-details?params={id:1234}
This works fine in practice but what concerns me is that any user can query my service using the above URL and retrieve another players details.
What would be the suggested way of guarding against such an issue? Would authorizers be the correct approach?
If the URL you posted is a mapped to CometD, then I strongly discourage you to use those kind of URLs to pass information such as params in the URL.
First, this will not work if you use other transports that are not HTTP, such as WebSocket.
Second, as you note that URL may expose information that you don't want to expose.
I recommend that you change the way you retrieve information from the server to not use URLs but only messages.
If all your communication with the server happens via messages, CometD on the server already validates that the message comes from a client that was allowed to handshake. You just need to enforce the right authentication checks at handshake time using a SecurityPolicy as explained the in authentication section.
The messages will have this form, using the CometD JavaScript client library:
cometd.publish("/service/player", { action:"get", playerId: 1234 });
There may be variations of this pattern where you want to put the "action" into the channel itself, for example:
cometd.publish("/service/player/get", { playerId: 1234 });
In this way, you have more little services (each responding to a different channel and therefore to a different action), which may be desirable.
Reading the examples of the services section may give you additional information.
I don't recommend to put the playerId into the channel for two reasons:
to avoid to create too many channels
to have this information promptly available in the code, so you don't need to parse the channel (although CometD support use of parameters in channels); parsing is more costly than just doing message.get("playerId").
To send the response to the client, the server can just call:
#Service
public class GetPlayer
{
#Session
private LocalSession sender;
#Listener("/service/player/get")
public void perform(ServerSession session, ServerMessage message)
{
Map<String, Object> player = retrievePlayerInfo(message.get("playerId"));
session.deliver(sender, message.getChannel(), player);
}
}
Note usage of ServerSession.deliver() to return the response to that specific client.
What above guarantees you (with a proper SecurityPolicy) that only authenticated clients can send and receive messages.
What you need to do now is to put in place the right authorizations, in particular that player 123 cannot play as player 789 by hacking the CometD messages that it sends.
This is the job for Authorizers, see the section and the examples in the Authorizers documentation.
What you must do is to establish a relation between the user that authenticated with the playerIds that she's allowed to see. That is application specific and it's the core of your Authorizer implementation.
With proper SecurityPolicy and Authorizers in place, your application is safe from the concerns of your question.
Strictly speaking, Authorizers may be enough, but typically if you want an authorization policy to be enforced, you also need authentication, which is provided by the SecurityPolicy.