Inject WebSocket in service REST - java

I need to send a message a client, after the creation of item. The item is created an ApiRest. Then I created my WebSocket with #ApplicationScope and I Injected in serviceREST with #Inject. The Problem is when the webSocket was initialized, in my serviceRest this webSocket's session still is null.
How I can use the web SOcket in my apirest?
#Path("citas")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class citaResource {
#Inject
com.softcase.citasmanager.websocket.ws websocket;
#GET
#Path("cita")
#Produces("application/json")
public Response cita() {
websocket.onMessage("Your Item was created");//Session of webSocket is null
return Response.ok("ItemCreated", MediaType.APPLICATION_JSON).build();
}
}
#ApplicationScope
#ServerEndpoint("/item")
public class ws{
private Session session;
#OnOpen
public void open(Session session) {
this.session = session;
}
#OnMessage
public void onMessage(String message) {
this.session.getBasicRemote().sendText(message);
}

A little context
Instances: there is a unique Session instance per client-server pair i.e. one instance of Session is created for a each client which connects to the WebSocket server endpoint. In short, the number of unique Session instances is equal to number of connected clients
Source: https://abhirockzz.gitbooks.io/java-websocket-api-handbook/content/lifecycle_and_concurrency_semantics.html
For more details: https://tyrus-project.github.io/documentation/1.13.1/index/lifecycle.html
A suggestion is to use a static variables like
// #ApplicationScope
#ServerEndpoint("/item")
public class ws{
// something like
private static final Set<javax.websocket.Session> ALL_SESSIONS = new HashSet<>();
// ...
}
An example can be found here. It's an option but I do not think it solves your injection issue.
Another option is to leverage the javax.websocket.Session#getOpenedSessions() method such as this chat example. But once again, it does not solve the injection issue.
Your example
You are using both websocket and REST. As I understand, the flow is:
User A, B, C are connected
User A submits a request to citas/cita and receives the REST response
At the same time, A, B, C receive a websocket notification
So, as you wrote, on one hand, you have
#Path("citas")
// ...
public class CitaResource{
// ...
}
and
// #ApplicationScope -> commented as irrelevant in your situation
#ServerEndpoint("/item")
public class ws{
// ...
}
In the example, there is one instance of CitaResource when user A made the request and three instances of ws as A, B, C are connected. However, you were right about the injection: you need to have something injected in CitaResource but you need a bean that is always available and as you noticed, websocket instances are not a good option and which session the container must inject?
A websocket sessions handler
The solution is to use an application scoped bean to handle all the existing sessions. I got it from Oracle tutorial. It goes like this:
// com.softcase.citasmanager.websocket.SessionHandler
#ApplicatedScoped
#Named // optional
public class MySessionHandler{
private final Set<Session> ALL_SESSIONS;
// or use a map if you need to identify the
// the session by a key. This example uses Set
// private final Map<String, Session> ALL_SESSIONS;
public MySessionHandler(){
ALL_SESSIONS = new HashSet<>();
}
// manage sessions
public void addSession(Session session){
this.ALL_SESSIONS.add(session);
}
public void removeSession(Session session){
this.ALL_SESSIONS.remove(session);
}
// send messages to all instances:
public void sendMessage(String message){
this.ALL_SESSIONS.stream()
// optional
.filter(s -> s.isOpen())
// or whatever method you want to send a message
.forEach( s -> s.getBasicRemote().sendText(message);
}
// or if you want to target a specific session
// hence my questions in comments
public void sendMessage(String message, String target){
this.ALL_SESSIONS..stream()
// identity the target session
.filter(s -> s.equals(target))
// optional
.filter(s -> s.isOpen())
.forEach( s -> s.getBasicRemote().sendText(message);
}
}
Note:
I optionally check that the stored session is still opened. isOpen() is not mandatory but it might avoid some errors
Think the session handler as the "captain": it knows everything about the websocket sessions whereas the sessions themselves do not know about each other.
However, you need to adapt your endpoint to make the session handler efficient:
// com.softcase.citasmanager.websocket.WsCita
#ServerEndpoint
public class WsCita{
// there is no need to declare the session as attribute
// private Session session;
// ApplicatedScoped so always defined
#Inject
private MySessionHandler handler;
#OnOpen
public void open(Session session){
handler.addSession(session); // "Aye cap'tain, reporting from duty!"
// your stuff
}
#OnClose
public void close(Session session, CloseReason closeReason){
handler.removeSession(session); // "Cya cap'tain, that's all for today!"
// your stuff
}
// your OnMessage and other stuff here
}
Now we have set our websocket architecture, what now?
You have one instance of WsCita per client. At any time, there might be zero, one or more instances.
MySessionHandler knows this information and is #ApplicatedScoped so it is safe to inject it
The REST endpoint then changes to:
#Path("citas")
// ...
public class citaResource {
#Inject
com.softcase.citasmanager.websocket.SessionHandler handler;
#GET
// ...
public Response cita() {
// REST processing
// ...
// Websocket processing:
// - Handler is always here for you
// - Handler knows which websocket sessions to send the message to.
// The RestController is not aware of the recipients
handler.sendMessage("Your Item was created");
}
}
Please note that I put the websocket processing after the REST processing as you may not always send the message (e.g. creation or whatever exception).
Misc
Unrelated to your questions but I have some comments about your code:
Classes name are CamelCase starting with capitalized letter per Oracle recommendation
Avoid generic name for your classes such as Ws. I renamed it WsCita for the example

Related

Passing data from object to parent in java

In my project I am trying to implement a event handling for my websocket instances. So I basically have 3 classes:
My AuthClient class which extends a WebsocketClient and has onMessage callback. In this onMessage method the websocket can enter different states like "hello", "pending_finish", "finish" and so on.
public class AuthClient extends WebSocketClient {
#Override
onMessage(String message) {
switch(message) ... // data events here
}
}
I am now trying to write a parent class called ClientManager where I want to store and manage multiple websockets.
// trying to receive data from specific data events for each client here
public class ClientManager {
private int maxClients;
private final Map<String, AuthClient> clients;
private final HashSet<String> tokens;
public ClientManager(HashSet<String> tokens, int maxClients) {
this.tokens = tokens;
this.maxClients = maxClients;
this.clients = new HashMap<>(maxClients);
}
}
My question is, how can I pass data which I receive from a WebsocketClient within this onMessage callback to my ClientManager? I need a solution that does not have any great performance loss.
I already tried implementing an interface with the events, but I don't quite get it to work with sharing data.
This looks like a good use case for an observer pattern.
Your observer would be your manager class and your observables would be your websocket clients. Any time you create a new websocket client, register your manager class instance with it using the registerObserver method. Then when you desire to forward a message from your websocket client to your manager, you can simply call the notifyObservers method to forward the request out to any registered observers. Take a look at the pattern info here:
https://www.baeldung.com/java-observer-pattern

Is there a way to create a global listener to receive messages from a Java websocket?

I'm working with AngularJS, Ionic and Java as backend. I'm using websocket to create a realtime chat.
I want to set up a listener to receive all the messages anywhere in my app to handler it and keep it without nodejs.
Is there a way to do it that?
I don't think there is a well prepared way to meet such requirements in Java WebSocket API. (Although, it would be possible with JMX without a little bit of effort.) However you can simply do that by writing a parent endpoint class, having every endpoint class extend it like the following:
// Assumes you use some dependency injection framework
#Component
// Your own POJO to be notified of every socket events
public class WebSocketListener {
public void onOpen(String id) {
// Your logic here
}
public void onMessage(String id, Object message) {
// Your logic here
}
public void onClose(String id) {
// Your logic here
}
}
// A parent class to notify your own global listener
// Extend it if you want to listen socket's event through some specific path
public abstract class NotifiableEndpoint {
#Inject
private WebSocketListener listener;
#OnOpen
public void onOpen(Session session) {
listener.onOpen(session.getId());
}
#OnMessage
public void onMessage(Session session, StringOrByteBuffer data) {
listener.onOpen(session.getId(), data);
}
#OnClose
public void OnClose(Session session) {
listener.onOpen(session.getId());
}
}
// A concrete class to write your business logic
#ServerEndpoint("/hello")
public class YourEndpoint extends NotifiableEndpoint {
// Your logic
}
Not to use inheritance, reflection through ServerEndpointConfig.Configurator.getEndpointInstance might work.

How to pass dynamic values to CXF OutInterceptor?

I have a webservice client using CXF and Spring.
The client proxy is created as spring managed #Bean, and registered to the SpringBus.
The soap webservice requires a HTTP header named "Auth=[pass]", whereas pass must be a dynamic value, unique for each user that uses the client.
Here is the problem: I created an AbstractOutDatabindingInterceptor that puts the HTTP header before sending the XML to the webservice.
Which works fine for hardcoded pass. But I need to obtain the pass inside the interceptor dynamically. Therefore, I would somehow have to pass the currently logged in user INTO the interceptor itself. So that the interceptor then could fetch the proper authorization pass.
#Configuration
public class AppConfig {
#Autowired
SpringBus bus;
#PostConstruct
public void init() {
bus.getOutInterceptors().add(new AbstractOutDatabindingInterceptor(Phase.MARSHAL) {
#Override
public void handleMessage(Message message) throws Fault {
Map<String, List<String>> headers = (Map<String, List<String>>) message.get(Message.PROTOCOL_HEADERS);
headers.put("Auth", Arrays.asList("dynamic pass per user"); //TODO how to get the current user dynamically?
}
});
}
#Bean
public WebservicePort getPort() {
WebservicePort port = new Webservice(bus);
return port;
}
}
#Service
public class WebserviceClient {
#Autowired
private WebservicePort port;
#Autowired
private AuthService authService;
public void run() {
//construct the xmlRequest
User user = authService.getCurrentUser();
port.send(xmlRequest); //invoke the auto-generated webservice stub
}
}
Of course I cannot change the auto generated method signature send(xml).
How can I get the current user into the OutInterceptor at time of sending dynamically, and just for this send request?
It must be threadsafe regarding the cxf client proxy, as multiple user might send a request simultaneously.

Implement RPC with underlying request/response protocol (JSON-RPC on SIP)

Look at the following use case.
I have a client (Java) application, which wants to get/set the state of another, remote application (C). The communication between them is done via SIP, which is run in another thread.
The SIP interface can do the following:
sendMessage
onRequest
I have two ideas for the architecture:
RPC (JSON-RPC)
Define a class which does the marshalling/unmarshalling for JSONRPCRequests and JSONRPCResponse (http://software.dzhuvinov.com/json-rpc-2.0-base.html)
Define a Invoker class, which has something like a call(server, name, arguments) method.
In the Invoker class, the name and arguments are put into a JSONRPCRequest and sent via the SIP layer sendMessage
Now comes my problem. How do i actually get the right back to the caller? The control flow is now:
The onRequest method is called, but I do now know whether it is the answer to my previous call. What i do is putting all responses reaching my server into a Map, and just poll that list in the Invoker.
A rough sketch might be;
Invoker (provides API to client)
class Invoker {
private Channel channel;
public Invoker(Channel channel) { this.channel = channel; }
public Object call(String server, String name, Object .. args) {
JSONRPCRequest req = ...;
channel.sendMessage(server, req.toString());
while( ! channel.hasResponse(req.id()) {
Thread.sleep(42);
}
return channel.getResponse(req.id()).result();
}
}
Channel (interface to messenger):
class Channel {
private Map<Object, JSONRPCResponse> responses = new //;
private Sip sip = new Sip() {
public void onRequest(String msg) {
JSONRPCResponse response = JSONRPCResponse.parse(msg);
responses.put(msg.id(), response);
}
};
public void sendMessage(String server, String message) {
sip.sendMessage();
}
public boolean hasResponse onRequest(Object id) {
responses.hasKey(id);
}
public JSONRPCResponse getResponse(Object id) {
responses.get(id);
responses.delete(id);
}
}
SIP (messenger itself):
abstract class Sip {
public void sendMessage(String msg) {
// SIP magic
}
public abstract void onRequest(String msg);
}
Is there a better way to do that? My biggest problems/code smells are:
the blocking in Invoker
the protocol is in Invoker, maybe I want to switch marshalling to something else
the map as mean to get the correct response for a request
the SIP abstract method looks strange
No error handling
No timeout
Message Passing
Is there an easy way to get rid of RPC, and implement something like RPC with just message passing? Any hints for pattern are welcome. I do not need the code itself, I am totally fine with just architecture. I tried to google for message passing implementations, and how they actually change state with it, but I did not find anything useful. How to implement timeout/ error handling?
Any good books/literature on that topic is also welcome, as I never programmed such distributed stuff.
Any other ideas on which protocol to use inside SIP to change state is welcome, too, as RPC was my initial thought, and I did not find anything other useful.
The code will not compile, I guess, it was just to visualize my idea.
Define a service interface that has meaningful (for the us case) methods. Instead of using blocking calls, have the client supply a ResponseHandler that will be invoked when the operation is complete:
interface ResponseHandler {
void onComplete(Response response);
void onError(Throwable error);
}
interface SomeService {
void set(String attribute, Object value, ResponsHandler responseHandler);
void get(String attribute, ResponseHandler responseHandler);
}
The implementation of the Service interface can use any suitable protocol, but it has to correlate requests with responses in order to invoke the correct callbacks.

RPC call - static methods is not working

I am trying to define a static method in the service interface to make an rpc call. But it doesn't allow me to do so. here I am pasting my code
Client class
public void sendDomesticData(String product,String dma,String yrmnths,String dist,String metrics) {
String url = GWT.getModuleBaseURL() + "domesticservice";
domesticServiceAsync = (DomesticServiceAsync) GWT.create(DomesticService.class);
ServiceDefTarget endpoint = (ServiceDefTarget) domesticServiceAsync;
endpoint.setServiceEntryPoint(url);
domesticServiceAsync.sendDomesticData(product,dma,yrmnths,dist,metrics,new Domestichandler<Void>() );
}
public class Domestichandler<Void> implements AsyncCallback<Void> {
#Override
public void onFailure(Throwable caught) {
String error = caught.getMessage();
System.out.println(error);
}
public void onSuccess(Void result) {
System.out.println("perfect");
}
}
Service
public interface DomesticService extends RemoteService {
public void sendDomesticData(String product,String dma,String yrmnths,String dist,String metrics);
}
public interface DomesticServiceAsync {
void sendDomesticData(String product,String dma,String yrmnths,String dist,String metrics,AsyncCallback<Void> callback);
}
Server side -
public void sendDomesticData(String product, String dma, String yrmnths, String dist, String metrics) {
System.out.println(product);
}
Basically I am trying to send the values from the front interface to the server side and I don't want any return value. But the values passed to the server side should be stored globally in the server class so i can access those values in different method. I tried changing all the senddomestic values to static but it won't allow me to do so? why?
Because RemoteServiceServlet needs to invoke your service methods somehow and the implementation expects instance methods. But this shouldn't prevent you from assigning the method data to static fields. Just be aware of multi threading.
GWT always uses instance methods for RPC calls, static methods are not possible in this case.
What is important to understand about GWT is that any RemoteServiceServlet instances are created and maintained by the servlet container (e.g. Tomcat). The servlet container might create a number of servlet instances on startup (Tomcat creates 6 RemoteServiceServlet instances by default) and then uses load balancing to determine which servlet handles an RPC request at a particular point in time. Depending on settings of course, you have little control over which RemoteServiceServlet instance exactly will handle a specific RPC request.
Therefore, if you want to store information on the server side globally using RPC calls, the idea proposed by YuPPie to use static fields of your RemoteServiceServlet implementation is a BAD idea. You will have no idea which of the RemoteServiceServlet instances maintained by the server contains your static data, and any subsequent calls to retrieve the data will give erratic results.
You have a few options, though. Storing the information in a database (or something similar) is the most straightforward option, but from your post I'm guessing you want something simpler. A singleton class which holds your data is probably the way to go. A thread-safe example:
public class DataContainer
{
private static DataContainer _singleton;
private String _dataField1;
public static synchronized DataContainer getInstance()
{
if (_singleton == null)
_singleton = new DataContainer();
return _singleton;
}
public synchronized String getDataField1()
{
return _dataField1;
}
public synchronized void setDataField1(String dataField1)
{
_dataField1 = dataField1;
}
}
Then in the server side implementation of your RPC call you could do something like:
public void sendDomesticData(String product, String dma, String yrmnths, String dist, String metrics)
{
DataContainer.getInstance().setDataField1(product);
}
This way, if there are multiple servlet instances they will all share the singleton instance of DataContainer, thus giving you a place to store your data globally. I hope this will help you.

Categories

Resources