This is probably obvious, but I am new to this paradigm. I create a Jetty Server and register my websocket class as follows:
Server server = new Server(8080);
WebSocketHandler wsHandler = new WebSocketHandler()
{
#Override
public void configure(WebSocketServletFactory factory)
{
factory.register(MyEchoSocket.class);
}
};
server.setHandler(wsHandler);
The websocket receives messages fine. I would like to also be able to send messages out from the server without having first received a message from the client. How do I access the MyEchoSocket instance that's created when the connection opens? Or, more generally, how do I send messages on the socket outside of the onText method in MyEchoSocket?
Two common techniques, presented here in a super simplified chatroom concept.
Option #1: Have WebSocket report back its state to a central location
#WebSocket
public class ChatSocket {
public Session session;
#OnWebSocketConnect
public void onConnect(Session session) {
this.session = session;
ChatRoom.getInstance().join(this);
}
#OnWebSocketMessage
public void onText(String message) {
ChatRoom.getInstance().writeAllMembers("Hello all");
}
#OnWebSocketClose
public void onClose(int statusCode, String reason) {
ChatRoom.getInstance().part(this);
}
}
public class ChatRoom {
private static final ChatRoom INSTANCE = new ChatRoom();
public static ChatRoom getInstance()
{
return INSTANCE;
}
private List<ChatSocket> members = new ArrayList<>();
public void join(ChatSocket socket)
{
members.add(socket);
}
public void part(ChatSocket socket)
{
members.remove(socket);
}
public void writeAllMembers(String message)
{
for(ChatSocket member: members)
{
member.session.getRemote().sendStringByFuture(message);
}
}
public void writeSpecificMember(String memberName, String message)
{
ChatSocket member = findMemberByName(memberName);
member.session.getRemote().sendStringByFuture(message);
}
public ChatSocket findMemberByName(String memberName)
{
// left as exercise to reader
}
}
Then simply use the central location to talk to the websockets of your choice.
ChatRoom.getInstance().writeSpecificMember("alex", "Hello");
// or
ChatRoom.getInstance().writeAllMembers("Hello all");
Option #2: Have WebSocket be created manually with WebSocketCreator
#WebSocket
public class ChatSocket {
public ChatRoom chatroom;
public ChatSocket(ChatRoom chatroom)
{
this.chatroom = chatroom;
}
#OnWebSocketConnect
public void onConnect(Session session) {
chatroom.join(this);
}
#OnWebSocketMessage
public void onText(String message) {
chatroom.writeAllMembers(message);
}
#OnWebSocketClose
public void onClose(int statusCode, String reason) {
chatroom.part(this);
}
}
public class ChatCreator implements WebSocketCreator
{
private ChatRoom chatroom;
public ChatCreator(ChatRoom chatroom)
{
this.chatroom = chatroom;
}
public Object createWebSocket(UpgradeRequest request,
UpgradeResponse response)
{
// We want to create the Chat Socket and associate
// it with our chatroom implementation
return new ChatSocket(chatroom);
}
}
public class ChatHandler extends WebSocketHandler
{
private ChatRoom chatroom = new ChatRoom();
#Override
public void configure(WebSocketServletFactory factory)
{
factory.setCreator(new ChatCreator(chatroom));
}
}
At this point you can use the same techniques as above to talk to the websockets of your choice.
Related
I have implemented a console application using Spring and WebSockets. The application works fine if one or more participants are connected to the base method which is anotated like this.
#MessageMapping("/chat")
#SendTo("/topic/messages")
I will copy the configuration and the implementation which i have made o be more clear.
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
}
#Controller
public class ChatController {
#MessageMapping("/chat")
#SendTo("/topic/messages")
public OutputMessage send(#Payload Message message) {
return new OutputMessage(message.getFrom(), message.getText());
}
#MessageMapping("/chat/{room}")
#SendTo("/topic/messages/{room}")
public OutputMessage enableChatRooms(#DestinationVariable String room, #Payload Message message) {
return new OutputMessage(message.getFrom(), message.getText());
}
}
#Service
public class SessionHandlerService extends StompSessionHandlerAdapter {
private String nickName;
public SessionHandlerService() {
this.nickName = "user";
}
private void sendJsonMessage(StompSession session) {
ClientMessage msg = new ClientMessage(nickName, " new user has logged in.");
session.send("/app/chat", msg);
}
#Override
public Type getPayloadType(StompHeaders headers) {
return ServerMessage.class;
}
#Override
public void handleFrame(StompHeaders headers, Object payload) {
System.err.println(payload.toString());
}
#Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
session.subscribe("/topic/messages", new SessionHandlerService());
sendJsonMessage(session);
}
}
The problem which i face is that when i subscribe to /topic/messages and session.send("/app/chat", msg);everything works fine. But if i choose something like session.send("/app/chat/room1", msg); and /topic/messages/room1 the participans can not see each other messages like they are in different chat rooms.
Hi i am trying to study named injection in Dagger2
Here are my Java classes but none of them seems to be working.
What i want is that based on #Named annotation i wish to get different objects.
public interface Server {
public void start();
public void stop();
public String request(String request);
}
public abstract class AbstractServer implements Server {
private boolean started;
#Override
public void start() {
started = true;
}
#Override
public void stop() {
if (!started) {
throw new IllegalStateException("Server was not started");
}
}
}
public class AudioServer extends AbstractServer{
#Override
public String request(String request) {
return "Response from Audio server: " + request;
}
}
public class VideoServer extends AbstractServer {
#Override
public String request(String request) {
return "Response from Video server: " + request;
}
}
#Module
public class ServerModule {
public ServerModule() {
}
#Provides
#Named("audio")
#Singleton
AudioServer provideAudioServer() {
return new AudioServer();
}
#Provides
#Named("video")
#Singleton
VideoServer provideVideoServer() {
return new VideoServer();
}
}
Please not ServerComponent.java is not compiling
#Singleton
#Component(modules = {ServerModule.class})
public interface ServerComponent {
AudioServer provideAudioServer();
VideoServer provideVideoServer();
void inject(TestInject inject);
}
public class TestInject {
private static final Logger logger = Logger.getLogger(TestInject.class.getSimpleName());
#Inject
#Named("audio")
Server audioServer;
public TestInject() {
// ServerComponent component = DaggerServerComponent.builder()
// .build();
// component.inject(this);
}
public void test() {
String serverResponse = null;
if (audioServer != null) {
serverResponse = audioServer.request("game.mp3");
logger.warning(serverResponse);
} else {
serverResponse = "Failure";
logger.info(serverResponse);
}
}
public static void main(String[] args) {
TestInject inject = new TestInject();
inject.test();
}
}
EDITED Please see answer in TestInject.java and ServerComponent.java
public interface Server {
public void start();
public void stop();
public String request(String request);
}
public abstract class AbstractServer implements Server {
private boolean started;
#Override
public void start() {
started = true;
}
#Override
public void stop() {
if (!started) {
throw new IllegalStateException("Server was not started");
}
}
}
public class AudioServer extends AbstractServer{
#Override
public String request(String request) {
return "Response from Audio server: " + request;
}
}
public class VideoServer extends AbstractServer {
#Override
public String request(String request) {
return "Response from Video server: " + request;
}
}
#Module
public class ServerModule {
public ServerModule() {
}
#Provides
#Named("audio")
#Singleton
AudioServer provideAudioServer() {
return new AudioServer();
}
#Provides
#Named("video")
#Singleton
VideoServer provideVideoServer() {
return new VideoServer();
}
}
Please not ServerComponent.java is not compiling
#Singleton
#Component(modules = {ServerModule.class})
public interface ServerComponent {
#Named("audio")
Server provideAudioServer();
#Named("video")
Server provideVideoServer();
void inject(TestInject inject);
}
public class TestInject {
private static final Logger logger = Logger.getLogger(TestInject.class.getSimpleName());
#Inject
#Named("audio")
Server audioServer;
#Inject
#Named("video")
Server videoServer;
public TestInject() {
ServerComponent component = DaggerServerComponent.builder()
.build();
component.inject(this);
}
public void testAudioServer() {
String serverResponse = null;
if (audioServer != null) {
serverResponse = audioServer.request("game.mp3");
logger.warning(serverResponse);
} else {
serverResponse = "audio server Failure";
logger.info(serverResponse);
}
}
public void testVideoServer() {
String serverResponse = null;
if (videoServer != null) {
serverResponse = videoServer.request("movie.mp4");
logger.warning(serverResponse);
} else {
serverResponse = "Video server Failure";
logger.info(serverResponse);
}
}
public static void main(String[] args) {
TestInject inject = new TestInject();
inject.testAudioServer();
inject.testVideoServer();
}
}
Your main problem seems to be related to the fact that you expect in the class TestInject a Server named audio while your provider returns AudioServer so dagger cannot satisfy your dependency.
Indeed don't forget that the annotation #Named is used to distinguish 2 objects of the same type, in other words you can annotate with #Named("audio") different providers as long as they don't return the same type. The object produced will then be identified by its type and its name.
So for example here is one way to fix you problem:
The class TestInject:
public class TestInject {
...
public TestInject() {
// Needed to inject your dependencies
ServerComponent component = DaggerServerComponent.builder()
.build();
component.inject(this);
}
...
}
The class ServerComponent
#Singleton
#Component(modules = ServerModule.class)
public interface ServerComponent {
void inject(TestInject inject);
}
The class ServerModule
#Module
public class ServerModule {
#Provides
#Named("audio")
#Singleton
public Server provideAudioServer() {
return new AudioServer();
}
#Provides
#Named("video")
#Singleton
public Server provideVideoServer() {
return new VideoServer();
}
}
Even with your question update, your module should be what I propose abose otherwise it won't compile for the same reason described previously.
I thought of creating a separate class for all of the smack's basic methods like connecting, login, sending message, receiving messages.
So, there's a listener method which receives messages.
static ChatManagerListener chatManagerListener = new ChatManagerListener() {
#Override
public void chatCreated(Chat chat, boolean createdLocally) {
chat.addMessageListener(
new ChatMessageListener() {
#Override
public void processMessage(Chat chat, Message message) {
System.out.println("MESSAGE RECEIVED: "+message.toString());
messageReceived(message);
}
});
}
};
Message is received and passed to messageReceived() method.
SITUATION:
Now, when I import this class into other, I would like to extend the functionality of this messageReceived() method, so the whole process remains abstract and the developer only deals with incoming messages. Or, somehow this messageReceived() method push the message to that other class.
Basically you need to define another listner to manage the message.
This is a working snippet example (of a prototype, so it's ugly and without any pattern) to update the GUI of reciver user.
If you'll need something else keep in mind that you'll need plugins (PacketInterceptor) on server side.
/*MessageGuiUpdate.java*/
public interface MessageGuiUpdate {
public void displayMessage(String body);
}
/*XmppManager.java*/
public void init() throws XMPPException, SmackException, IOException {
private MessageGuiUpdate guiUpdate;
//FOO
//BAR
/* init() */
this.chatManager = ChatManager.getInstanceFor(connection);
this.chatManager.addChatListener(
new ChatManagerListener() {
#Override
public void chatCreated(Chat chat, boolean createdLocally)
{
if (!createdLocally)
{
chat.addMessageListener(new IncomingMessageListener());;
}
}
});
}
/*nested class*/
class IncomingMessageListener implements ChatMessageListener {
#Override
public void processMessage(Chat arg0, Message message) {
String from = message.getFrom();
String body = message.getBody();
if (body != null)
{
System.out.println(String.format("============ Received message '%1$s' from %2$s\n============", body, from));
guiUpdate.displayMessage(body);
}
}
}
/*CustomGui.java*/
public class CustomGui implements MessageGuiUpdate {
//foo
#Override
public void displayMessage(String message) {
System.out.println("I've just recived: "+message);
}
}
I use Grizzly to setup websocket server in java. My problem is that I can't set broadcaster to websocket to use optimized one by overriding WebSocketApplication. I followed the way the documentation describe. But I got that error.
The method setBroadcaster(Broadcaster) from the type SimpleWebSocket
is not visible.
Here is my code
public class BroadcastApplication extends WebSocketApplication {
private final Broadcaster broadcaster;
public BroadcastApplication(Broadcaster broadcaster) {
this.broadcaster = broadcaster;
}
#Override public WebSocket createSocket(ProtocolHandler handler,
HttpRequestPacket requestPacket, WebSocketListener... listeners) {
final DefaultWebSocket ws = (DefaultWebSocket) super.createSocket(handler, requestPacket, listeners);
ws.setBroadcaster(broadcaster); // Got error here
return ws;
}
#Override public void onMessage(WebSocket socket, String data) {
socket.broadcast(getWebSockets(), data);
}
}
My grizzly-websockets version is 2.3.22. Any suggestion is welcome.
Something like this should work:
public class BroadcastApplication extends WebSocketApplication {
private final Broadcaster broadcaster;
public BroadcastApplication(Broadcaster broadcaster) {
this.broadcaster = broadcaster;
}
#Override
public WebSocket createSocket(ProtocolHandler handler,
HttpRequestPacket requestPacket, WebSocketListener... listeners) {
return new BroadcastWebSocket(broadcaster, handler, requestPacket, listeners);
}
#Override
public void onMessage(WebSocket socket, String data) {
socket.broadcast(getWebSockets(), data);
}
private static class BroadcastWebSocket extends DefaultWebSocket {
public BroadcastWebSocket(Broadcaster broadcaster,
ProtocolHandler protocolHandler,
HttpRequestPacket request,
WebSocketListener... listeners) {
super(protocolHandler, request, listeners);
this.broadcaster = broadcaster;
}
}
}
I'm new with akka, so want to ask how to add event to event bus and catch it called. There is code sample.
localEventBus = new com.google.common.eventbus.EventBus(new ExceptionHandler());
Object listener = new Object()
{
#Subscribe
public void witingCommand(SomeCommand cmd)
{
//Here I want to catch call
}
};
...
localEventBus.register(listener);
...
client = system.actorOf(Props.create(ClientActor.class, localEventBus, receptionists));
...
client.tell(new Publish(Constants.COMMAND, someCommand), noSender());
Types:
private final ActorSystem system;
private ActorRef client;
ClientActor.java
final class ClientActor extends UntypedActor
{
ClientActor(final EventBus eventBus, final List<ActorSelection> receptionists)
{
localEventBus = eventBus;
clusterReceptionists.addAll(receptionists);
}
#Override
public void preStart()
{...}
#Override
public void postStop()
{...}
#Override
public void onReceive(final Object msg)
{...}
}
Problem is that 'witingCommand' is not being called, maybe someone can tell me what I do wrong?