Cometd Client not handshaking with cometD Server - java

I am new to CometD.I have a written a basic CometD Server in java and simple CometD Client.I am getting Successful response from postman for /meta/handshake,/meta/connect,/meta/subscribe channels. But when i start using my cometD java client(which i reused from the https://protect-us.mimecast.com/s/vLH6CNk58of1Ow1GsmVz4u?domain=github.com), handshake is failing with the below message.
Failing {supportedConnectionTypes=[long-polling],
channel=/meta/handshake, id=22, version=1.0}
I am using cometdVersion - '4.0.0',jettyVersion - '9.4.0.v20161208', springbootVersion - '1.5.14.RELEASE' in my code.
I have done a dynamic servlet registration to AnnotationCometDServlet and added /notifications as mapping.
I have created a channel as below in bayeuxServer configuration class.
bayeuxServerImpl.createChannelIfAbsent("/updates",
(ServerChannel.Initializer) channel -> channel.addAuthorizer(GrantAuthorizer.GRANT_ALL));
In client code, i have used /notifications as the defaulturl and channel as /updates
#Service("cometListener")
#Slf4j
public class BayeuxListener implements BayeuxServer.SessionListener {
#Inject
private BayeuxServer bayeuxServer;
#Session
private ServerSession serverSession;
#Configure({"/updates**,/notifications**"})
protected void configureChannel(ConfigurableServerChannel channel) {
channel.addAuthorizer(GrantAuthorizer.GRANT_ALL);
channel.addAuthorizer(GrantAuthorizer.GRANT_PUBLISH);
channel.setPersistent(true);
}
#Listener("/meta/*")
public void monitorMeta(ServerSession session, ServerMessage message) {
log.info("monitoring meta"+message.toString()+"channel "+message.getChannel()+"session id "+session.getId());
}
#Listener("/meta/subscribe")
public void monitorSubscribe(ServerSession session, ServerMessage message) {
log.info("Monitored Subscribe from " + session + " for " + message.get(Message.SUBSCRIPTION_FIELD));
}
#Listener("/meta/unsubscribe")
public void monitorUnsubscribe(ServerSession session, ServerMessage message) {
log.info("Monitored Unsubscribe from " + session + " for " + message.get(Message.SUBSCRIPTION_FIELD));
}
#Listener("/updates")
public void handlesrgUpdates(ServerSession client, ServerMessage message) {
ServerSession cilentSession = bayeuxServer.getSession(client.getId());
client.deliver(cilentSession,"/updates", "Received message back from client");
}
}

You have a strange combination of CometD version, Jetty version and Spring Boot version. I recommend that you stick with the default versioning declared in the CometD POM, i.e. CometD 4.0.2, Jetty 9.4.14 and Spring Boot 2.0.6.
The handshake failure you mention is not complete or it is not a failed handshake reply. This is because handshake replies have the successful field, and what you mention {supportedConnectionTypes=[long-polling], channel=/meta/handshake, id=22, version=1.0} looks like the handshake request. As such it's difficult to say what the problem is, because the failure reason is typically reported in the handshake reply.
If you have dynamically registered the CometD Servlet under the /notifications Servlet mapping, then the client should have a URL that ends with /notifications.
Note that the Servlet mapping /notifications and the CometD channel /notifications are two different things and are not related - they just happen to have the same name.
Your code is mostly fine, but contains a few errors.
#Configure({"/updates**,/notifications**"})
protected void configureChannel(ConfigurableServerChannel channel) {
channel.addAuthorizer(GrantAuthorizer.GRANT_ALL);
channel.addAuthorizer(GrantAuthorizer.GRANT_PUBLISH);
channel.setPersistent(true);
}
The code above must instead be:
#Configure({"/updates/**,/notifications/**"})
protected void configureChannel(ConfigurableServerChannel channel) {
channel.addAuthorizer(GrantAuthorizer.GRANT_ALL);
channel.setPersistent(true);
}
Note that the channel globbing must be after a /.
There is no need to GRANT_PUBLISH after a GRANT_ALL, which include GRANT_PUBLISH.
The configuration method should be public, not protected.
#Listener("/updates")
public void handlesrgUpdates(ServerSession client, ServerMessage message) {
ServerSession cilentSession = bayeuxServer.getSession(client.getId());
client.deliver(cilentSession,"/updates", "Received message back from client");
}
There is no need to retrieve clientSession from bayeuxServer because it is already been passed as parameter client to the method.
The method can be better implemented as:
#Listener("/updates")
public void handlesrgUpdates(ServerSession client, ServerMessage message) {
client.deliver(serverSession, "/updates", "Received message back from client");
}
Note how the "sender" of the message is the serverSession reference that has been injected as a field of the class.
The code above is still possibly wrong.
Because /updates is a broadcast channel, if the client is subscribed to the /updates channel, when the client publishes a message to the /updates channel it will receive it back from the server (because the client is subscribed to the /updates channel) and the code above will also send another message to the client on the /updates channel via deliver(), so the client will receive two different messages on the /updates channel.
This may be what you want, but most of the times it's not.
Please have a read at the difference between broadcast channels and service channels.
Update the question with the details of your handshake failure, and use consistent versioning for CometD, Jetty and Spring Boot.

Related

gRPC client not working when called from within gRPC service

Quick disclaimer, I am very new to gRPC and RPC in general, so please have patience
I have two gRPC servers running on the same java application, Service A and Service B.
Service A creates multiple clients of Service B which then synchronously makes calls to the various instances of Service B
The server
Service A has a rpc call defined by the .proto file as
rpc notifyPeers(NotifyPeersRequest) returns (NotifyPeersResponse);
the server side implementation,
#Override
public void notifyPeers(NotifyPeersRequest request, StreamObserver<NotifyPeersResponse> responseObserver) {
logger.debug("gRPC 'notifyPeers' request received");
String host = request.getHost();
for (PeerClient c : clients.values()) {
c.addPeer(host); // <---- this call
}
NotifyPeersResponse response = NotifyPeersResponse.newBuilder()
.setResult(result)
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
The list of peers, clients are built up in previous rpc calls.
ManagedChannel channel = ManagedChannelBuilder.forTarget(peer).usePlaintext().build();
ClientB client = new ClientB(channel);
clients.put(peer, client);
The client
rpc addPeer(AddPeerRequest) returns (AddPeerResponse);rpc addPeer(AddPeerRequest) returns (AddPeerResponse);
the server side implementation,
#Override
public void addPeer(AddPeerRequest addPeerRequest, StreamObserver<AddPeerResponse> responseObserver) {
logger.info("gRPC 'addPeer' request received");
boolean result = peer.addPeer(host);
AddPeerResponse response = AddPeerResponse.newBuilder()
.setResponse(result)
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
the client side implementation,
public boolean addPeer(String host) {
AddPeerRequest request = AddPeerRequest.newBuilder().setHost(host).build();
logger.info("Sending 'addPeer' request");
AddPeerResponse response = blockingStub.addPeer(request);
return response.getResponse();
}
When I run this application, and an RPC is made to Service A and the client connection is created that calls addPeer, an ambiguous exception is thrown, io.grpc.StatusRuntimeException: UNKNOWN which then causes the JVM to shut down. I have no idea how to fix this, or whether it is even possible to create an gRPC client connection within a gRPC server
for all of my gRPC server implementations I'm using blocking stubs.
<grpc.version>1.16.1</grpc.version>
<java.version>1.8</java.version>
I've pretty much hit a brick wall, so any information will be appreciated
The UNKNOWN message is an exception on the server side that was not passed to the client.
You probably need to increase the log level on the server to try to find the root cause.
In this post here ,
creating the channel like below, enable it to see a more meaningful error message:
ManagedChannel channel = NettyChannelBuilder.forAddress( host, port )
.protocolNegotiator(ProtocolNegotiators.serverPlaintext() )
If A and B are in the same application have you considered making direct function calls or at least using the InProcessChannelBuilder and InProcessServerBuilder?
As mentioned elsewhere, in the current setup you can try increasing the log level on the server side (in B) to see the source of the exception.

How can I broadcast a message to all clients connected to an Undertow websocket server?

This is my current setup for an Undertow websocket server:
Undertow server = Undertow.builder()
.addHttpListener(8080, "localhost")
.setHandler(path()
.addPrefixPath("/", websocket((exchange, channel) -> {
channel.getReceiveSetter().set(new AbstractReceiveListener() {
#Override
protected void onFullTextMessage(WebSocketChannel channel, BufferedTextMessage message) {
final String messageData = message.getData();
for (WebSocketChannel session : channel.getPeerConnections()) {
WebSockets.sendText(messageData, session, null);
}
}
});
channel.resumeReceives();
}))).build();
This is copied from one of their demo files. I believe onFullTextMessage here broadcasts any messages it receives to all clients.
I want to be able to trigger this behavior on demand. So instead of having to receive a message and using an event handler to send out more messages, I want to be able to arbitrarily do server.send() and send a message to all connected clients.
I've looked around and haven't found anything that seems capable of achieving this. I don't relish the idea of tracking all WebSocketChannels and sending to each client manually. Surely, there's a method somewhere that I've overlooked?
I'd appreciate some pointers if it's not something that's just not possible!
You can broadcast the message to all the clients on the channel by getting all the connections to this canal and sending the message :
channel.getPeerConnections().forEach(connection -> {
WebSockets.sendText(messageData, connection, null);
});

My netty client program is not receiving any response from server

I have written a client-server programme using netty IO in which I am able to send a message to the server and server responds back but unfortunately, any of the listener methods in my Client Handler class is not getting invoked ie.. response is not received at the client level back. This is the piece of code used in my client to send a request message to the server.
ChannelFuture cf =this.ctx.channel().writeAndFlush(Unpooled.copiedBuffer(getEchoISOMessage(), CharsetUtil.ISO_8859_1));
this.ctx.writeAndFlush(Unpooled.EMPTY_BUFFER);
cf.awaitUninterruptibly();
if (!cf.isSuccess()) {
System.out.println("Send failed: " + cf.cause());
}
Below is the code used to receive any response message from the server.
#Override
public void ChannelRead(ChannelHandlerContext ctx, ByteBuf in)
{
System.out.println("Received a response from Server..");
String responseMessage="";
responseMessage=in.toString(CharsetUtil.ISO_8859_1);
System.out.println("Received data as echo response"+responseMessage+"from "+ctx.channel().remoteAddress());
}
But this method is not getting invoked at all.
What does your handler extend from? It should be an inbound handler if it is to receive a response from the server. Are you sure your code compiles with the #Override annotation? The callback method in ChannelInboundHanlder is public void void channelRead(ChannelHandlerContext ctx, Object msg). Make sure your annotate with #Override and make sure the code compiles. Then your client will/should receive the response from the server. If it doesn't help, consider sharing the full code.

Websocket ClientEndpoint OnMessage-method not called when large String (length 20000) is sent

1) Using javax.websocket.ClientEndpoint and javax.websocket.OnMessage with JavaSE-1.8
2) Server (built with Annotations) sends String with length 20000:
#ServerEndpoint("/websocket")
public class MyEndpoint {
#OnMessage
public String onMessage(String message, Session session) {
session.getBasicRemote().sendText( "eg String with length 20000 .... ... ..." );
}
}
3) Client sends request and should receive String from server (example just receives):
#ClientEndpoint
public class Receiver {
#OnMessage
public void processMessage(String message, Session session) {
System.out.println("Message received: " + message );
}
}
But:
No Message is received by client. Shorter strings eg length 500 work.
No exception is raised at server (if try-catch used).
So - the sent string with Length 20000 disappears without any error !
A browser which sends the request to the server receives the string from the server and everything works, so problem has to be at client.
Also the implementation org.java_websocket.client.WebSocketClient receives the String from the server.
I also tried to set MaxTextMessageBufferSize with
session.setMaxTextMessageBufferSize(25000);
Where is the bug?
Where do i have to report this bug to get a working client for larger strings?
The implementation of javax.websocket is pretty nice with its session-IDs and i do not want to wrap org.java_websocket.client.WebSocketClient just because of this single bug in javax.websocket.ClientEndpoint ?
Thanks.
Sounds like a server configuration issue or bug.
(Hard to narrow down how to help, as you didn't tell us what kind of server you are using)
Most modern javax.websocket servers can handle multi-megabyte message sizes with ease.
These work great:
Jetty 9.2.x+
Tomcat 8.x+
Wildfly/JBoss 8.x+
All of those have excellent websocket support and configuration options.

Sending multiple messages to a server using Java Websockets Client side?

#OnOpen
public void onOpen(Session session) {
session.getBasicRemote.sendText("Echo me");
session.addMessageHandler(new MessageHandler.Whole<String>() {
public void onMessage(String arg0) {
System.out.println(arg0);
}
});
}
I send a message, and it's echoed back to me. Well what if I wanted to send another message?
How would I do that?
The reason I ask is because I'm logging in to a server, and they send me back a message stating if I'm successfully logged in or not.
After I've been successfully logged in, I want to send another message querying the server about different information I require.
WebSocketContainer.connectToServer(...) method returns Session object. When client is connected, you can use this to send messages (see Session.getBasicRemote().sendText(...) or Session.getBasicRemote().sendBinary(...)).

Categories