How to send parameters from CometD client to CometD server - java

I have a SessionListener on a CometD server. I want to pass data from a client to the server when the listener's sessionAdded() method is called.
The sessionAdded() method receives a ServerSession and ServerMessage object. ServerSession has an Attribute map that always seems to have nothing in it.
I would like to get some unique client data to the server. This data should be accessed by the server when the sessionAdded() method is invoked.
The documentation talks about basic use of a SessionListener, but says nothing about attributes. All the javadocs for client and server say about it is to describe how setAttribute() sets an attribute and how getAttribute() gets it.
Is there a way to do this? Can the ServerSession's attribute map be used to transfer attributes from the client to the server, and if so, how?
Someone please advise...

The ServerSession attributes map is a map that lives on the server.
It is an opaque (from the CometD point of view) map that applications can populate with whatever they need.
If you want to send data from a client to the server, you can just put this additional data into the handshake message, and then retrieve it from the message when BayeuxServer.SessionListener.sessionAdded() is called.
The client looks like this:
BayeuxClient client = ...;
Map<String, Object> extraFields = new HashMap<>();
Map<String, Object> ext = new HashMap<>();
extraFields.put(Message.EXT_FIELD, ext);
Map<String, Object> extraData = new HashMap<>();
ext.put("com.acme", extraData);
client.handshake(extraFields);
extraData.put("token", "foobar");
This creates an extra data structure that in JSON looks like this:
{
"ext": {
"com.acme": {
"token": "foobar"
}
}
}
It is always a very good practice to put your data under a namespace such as com.acme, so that you don't mess up with CometD fields, nor with other extensions that you may use.
Put your fields inside extraData, like for example field token in the example above.
Then, on the server:
public class MySessionListener implements BayeuxServer.SessionListener {
#Override
public void sessionAdded(ServerSession session, ServerMessage message) {
Map<String, Object> ext = message.getExt();
if (ext != null) {
Map<String, Object> extra = (Map<String, Object>)ext.get("com.acme");
if (extra != null) {
String token = (String)extra.get("token");
session.setAttribute("token", token);
}
}
}
#Override
public void sessionRemoved(ServerSession session, boolean timedout) {
}
}
This listener puts into the session attributes data that has been sent by the client, in the example above the token field.
Then, elsewhere in the application, you can access the session attributes and use that data.

Related

logging id in quarkus vertx reactive

how to logging with a id in quarkus vertx reactive ?
I want to see processing steps from request to response with the same id in the log.
Although each component is a different thread.
I'm afraid there's no built-in concept of a request ID, and you'll have to generate your IDs yourself. One solution could be that you use an AtomicLong instance to generate a request ID for each request.
Then, to store and access the ID, you basically have two options.
First option: you can store that ID in the request's context by having
#Inject
CurrentVertxRequest request;
(...)
request.getCurrent().put("requestId", id);
And then various components that produce logs can access the ID by
request.getCurrent().get("requestId");
and add that to the log message.
Second option: if you want to avoid the mess of having to append the ID in each log message manually, you can add it to the Mapped Diagnostic Context (MDC). The problem with this is that the MDC context is not propagated by default, so to make sure that each thread sees the ID, you'll need a custom ThreadContextProvider like this:
public class MdcContextProvider implements ThreadContextProvider {
#Override
public ThreadContextSnapshot currentContext(Map<String, String> props) {
Map<String, String> propagate = MDC.getCopyOfContextMap();
return () -> {
Map<String, String> old = MDC.getCopyOfContextMap();
MDC.setContextMap(propagate);
return () -> {
MDC.setContextMap(old);
};
};
}
#Override
public ThreadContextSnapshot clearedContext(Map<String, String> props) {
return () -> {
Map<String, String> old = MDC.getCopyOfContextMap();
MDC.clear();
return () -> {
MDC.setContextMap(old);
};
};
}
#Override
public String getThreadContextType() {
return "MDC";
}
}
and add a META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider file containing the qualified name of that class.
Then, store the request ID in the MDC using
MDC.put("rid", requestId);
And change the formatting string of your logs (for example, the quarkus.log.console.format property) to contain a reference to it, which would be %X{rid}, to make sure that this value is added to each log.
With this option you should probably also make sure that the MDC entry gets cleared when the request processing is done.
So this option is unfortunately much more complicated, but will potentially help keep your code cleaner, because you won't have to append the ID to each log.

How to get POJO from a volley callback, and use/return it in the calling function?

I'm implementing a simple mobile app with user accounts. Additionally, it must be structured in a layered architecture that cleanly separates presentation, logic and access to the database.
I'm currently able to send and get data from a server, using the volley library. However, this data is only available inside the onResponse method of the Response.Listener<String> passed as a parameter in the constructor of stringRequest object, later used to perform the request. I want to use the data that I get in the response to construct a User object that I could use all over my app, and keep the layered architecture as much as possible.
This is an example of the kind of method I've been aiming for:
public ResponseType insertUser (final Context context, final String id, final String name, final String password) {
//using a wrapper object because have to declare object as final to use
//inside inner class, so use field to assign value
final ResponseWrapper wrapper = new ResponseWrapper();
StringRequest stringRequest = new StringRequest(Request.Method.POST, BuildConfig.ip,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
wrapper.response = response.equals("") ?
ResponseWrapper.SUCCESS :
ResponseWrapper.DB_ERROR;
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
wrapper.response = ResponseWrapper.CONNECTION_ERROR;
}
}){
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> params = new HashMap<String,String>();
params.put("id",id);
params.put("name",name);
params.put("password",password);
return params;
}
};
RequestQueue requestQueue = Volley.newRequestQueue(context);
requestQueue.add(stringRequest);
//waiting for callback to modify field
while(wrapper.response == null);
return wrapper.response;
}
I've tried setting a field of an external object inside onResponse, waiting for the field to be changed to continue execution, to no avail. The code compiles and performs the request, but the field is kept unchanged. My research has suggested to me that this is something to be expected when dealing with asynchronous code.
Most examples I've read limit their scope to using Toast to show the response in the screen. A couple change activities inside the method, but this goes against the layer separation that I'm trying to achieve by performing presentation actions inside the database access layer (and potentially performing business logic too, as my app becomes more complex).
So, how can I get an object from inside the callback? (For example the String containing the response, or an enum indicating the result of an operation).
If this isn't possible or advisable, how could I structure the code to keep the separation of concerns?
My thanks in advance for any suggestion that could steer me in the right direction.

How to retrieve properties of IoT hub data and not only the actual payload

I have a iot-hub that receives both JSON and non-json (hex) messages. These all go to my Java function app to decode. Based on the device-id I'm calling a different decoder.
I'm trying to get the actual iothub-connection-device-id of the message I'm receiving.
public class TranslateEndpoint {
/**
* This function will be invoked when an event is received from Event Hub.
*/
#FunctionName("TranslateEndpoint")
public void run(
#EventHubTrigger(name = "message", eventHubName = "NAME-DeviceIntegration", connection = "HostName=HOST;SharedAccessKeyName=NAME;SharedAccessKey=KEY=", consumerGroup = "$Default", cardinality = Cardinality.ONE) EventData message,
final ExecutionContext context
) {
context.getLogger().info("Java Event Hub trigger function executed.");
context.getLogger().info("Length:" + message.toString());
TranslateController temp = new TranslateController();
// Build up a list with all the data
context.getLogger().info(message.getSystemProperties().getPublisher());
context.getLogger().info(message.getSystemProperties().getPartitionKey());
context.getLogger().info(message.getSystemProperties().get("iothub-connection-device-id").toString());
}
The code above is inspired by some C# code I found. Unfortunately I get an error
Stack: java.lang.RuntimeException: Unable to invoke no-args constructor for interface com.microsoft.azure.eventhubs.EventData. Registering an InstanceCreator with Gson for this type may fix this problem.
Before I used a String and only got my actual payload. What is the proper way of receiving system properties of my message?
At least in Java, metadata properties have to be pulled in through additional, annotated parameters, in this case:
//The system properties, including the event data
#BindingName("SystemProperties") Map<String, Object> systemProperties
The device ID can then be retrieved from that parameter:
String deviceId = (String) systemProperties.get("iothub-connection-device-id");
The message parameter annotated with #EventHubTrigger should be a string or maybe a byte array.
A POJO (or EventData for that matter) cannot be mapped in this case, as the backing data only contains the event payload/value.
So, the function should look like this:
public class TranslateEndpoint {
/**
* This function will be invoked when an event is received from Event Hub.
*/
#FunctionName("TranslateEndpoint")
public void run(
#BindingName("SystemProperties") Map<String, Object> systemProperties,
#EventHubTrigger(name = "message", eventHubName = "NAME-DeviceIntegration", connection = "HostName=HOST;SharedAccessKeyName=NAME;SharedAccessKey=KEY=", consumerGroup = "$Default", cardinality = Cardinality.ONE) String message,
final ExecutionContext context
) {
String deviceId = (String) systemProperties.get("iothub-connection-device-id");
//decode/parse message string
//...
}
}
Documentation:
event metadata binding: https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java#metadata
event metadata properties: https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-event-iot#trigger---event-metadata
system properties in IoT Hub messages: https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-messages-construct
I can't help but say that the documentation could be less cumbersome, just like the APIs in all supported languages could be more consistent. I might still be missing something.

Detect destination channel of SessionUnsubscribeEvent

My Situation
I'm building a small web chat to learn about Spring and Spring WebSocket. You can create different rooms, and each room has it's own channel at /topic/room/{id}.
My goal is to detect when users join and leave a chat room and I thought I could use Spring WebSocket's SessionSubscribeEvent and SessionUnsubscribeEvent for this.
Getting the Destination from the SessionSubscribeEvent is trivial:
#EventListener
public void handleSubscribe(final SessionSubscribeEvent event) {
final String destination =
SimpMessageHeaderAccessor.wrap(event.getMessage()).getDestination();
//...
}
However, the SessionUnsubscribeEvent does not seem to carry the destination channel, destination is null in the following snippet:
#EventListener
public void handleUnsubscribe(final SessionUnsubscribeEvent event) {
final String destination =
SimpMessageHeaderAccessor.wrap(event.getMessage()).getDestination();
//...
}
My Question
Is there a better way to watch for subscribe/unsubscribe events and should I even be using those as a way for a user to "log in" to a chat room, or should I rather use a separate channel to send separate "log in"/"log out" messages and work with those?
I thought using subscribe/unsubscribe would've been very convenient, but apparently Spring makes it very hard, so I feel like there has to be a better way.
STOMP Headers only appear in the frames relevant to your question as described here: https://stomp.github.io/stomp-specification-1.2.html#SUBSCRIBE and here: https://stomp.github.io/stomp-specification-1.2.html#UNSUBSCRIBE
Only the SUBSCRIBE frame has both destination and id, the UNSUBSCRIBE frame has only an id.
This means you have to remember the subscription id with the destination for future lookup. Care must be taken because different Websocket connections usually use/assign the same subscription ids, so to save destinations reliably, you have to include the websocket session id in your storage key.
I wrote the following method to get it:
protected String getWebsocketSessionId(StompHeaderAccessor headerAccessor)
{
// SimpMessageHeaderAccessor.SESSION_ID_HEADER seems to be set in StompSubProtocolHandler.java:261 ("headerAccessor.setSessionId(session.getId());")
return headerAccessor.getHeader(SimpMessageHeaderAccessor.SESSION_ID_HEADER).toString();
}
StompHeaderAccessor is created like this:
StompHeaderAccessor headerAccessor=StompHeaderAccessor.wrap(((SessionSubscribeEvent)event).getMessage());
StompHeaderAccessor headerAccessor=StompHeaderAccessor.wrap(((SessionUnsubscribeEvent)event).getMessage());
This can then be used to create a unique subscription id which can be used as a key for a map to save data about the subscription, including the destination:
protected String getUniqueSubscriptionId(StompHeaderAccessor headerAccessor)
{
return getWebsocketSessionId(headerAccessor)+"--"+headerAccessor.getSubscriptionId();
}
Like this:
Map<String, String> destinationLookupTable=...;
// on subscribe:
destinationLookupTable.put(getUniqueSubscriptionId(headerAccessor), destination);
// on other occasions, including unsubscribe:
destination=destinationLookupTable.get(getUniqueSubscriptionId(headerAccessor));
I think using SessionSubscribeEvent and SessionUnsubscribeEvent is a good idea for that matter. You can get the destination if you keep track of the SessionID:
private Map<String, String> destinationTracker = new HashMap<>();
#EventListener
public void handleSubscribe(final SessionSubscribeEvent event) {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(event.getMessage());
destinationTracker.put(headers.getSessionId(), headers.getDestination());
//...
}
#EventListener
public void handleUnsubscribe(final SessionUnsubscribeEvent event) {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(event.getMessage());
final String destination = destinationTracker.get(headers.getSessionId());
//...
}

Using JAX-WS: How can I set the user agent property

I've searched on this and found a few near misses. I've created a java client to consume a web service using JAX-WS. Is there a way when using JAX to set the HTTP_USER_AGENT value? I would like to have my web service log when specific clients (mine) access it so I wanted a customized value.
I've seen options where you set it in the system properties but this doesn't seem to work. The generated JAX classes don't seem to have a direct reference to the connection object so I don't see how I can manipulate those classes.
Any help would be great.
Thanks
ST
The solution to this kind of problem in JAX-WS is to implement a SoapMessage Handler (Interface: SOAPHandler< SOAPMessageContext >).
Within that handler you insert your HTTP header into maybe already existing headers, then you give control to the next handler in the handler chain.
The concept of this handler chain is kind of nice, you can have small classes for a very specific purpose (Security, Logging etc.).
In your client you configure the handler chain prior to sending any request:
// HandlerChain installieren
Binding binding = ((BindingProvider) port).getBinding();
List hchain = binding.getHandlerChain();
if (hchain == null) {
hchain = new ArrayList();
}
hchain.add(new HTTPUserAgentHandler());
binding.setHandlerChain(hchain);
And here is the code for the HTTPUserAgentHandler:
public class HTTPUserAgentHandler implements SOAPHandler<SOAPMessageContext> {
#Override
public boolean handleMessage(SOAPMessageContext context) {
boolean request = ((Boolean) context.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY)).booleanValue();
if (request) {
#SuppressWarnings("unchecked")
Map<String, List<String>> headers = (Map<String, List<String>>) context
.get(MessageContext.HTTP_REQUEST_HEADERS);
if (null == headers) {
headers = new HashMap<String, List<String>>();
}
headers.put("HTTP_USER_AGENT", Collections.singletonList("user_agent"));
context.put(MessageContext.HTTP_REQUEST_HEADERS, headers);
}
return true;
}
#Override
public boolean handleFault(SOAPMessageContext context) {
return true;
}
#Override
public void close(MessageContext context) {}
#Override
public Set<QName> getHeaders() {
return null;
}
}
Let me question the idea of having HTTP header first.
A more correct (WS-centric) approach is to set SOAP Header, not HTTP header. Consider this: SOAP messages can be delivered not only by HTTP, but by JMS, SMTP or custom transports. By requiring to have user-agent HTTP Header, you unnecessary tie you code to only one transport, albeit currently prevailing.
This is the reason BTW why JAX-WS have no notion of HTTP headers except in handlers.
And (of course) StackOverlow knows how to create SOAP headers.
not sure if this is the best/most direct way to do it, but i think you could add a custom javax.xml.ws.handler.Handler to the handler chain in the dispatch javax.xml.ws.Binding. in the Handler, you should be able to set a custom map of extra http headers on the outgoing MessageContext using the MessageContext.HTTP_REQUEST_HEADERS property.

Categories