I'm currently using Struts 2 as my framework and I need to have a Websocket feature so I can communicate with my client that is accessing it through HTML Websocket.
I have tried to use Java Websocket API (JSR 356) with Java application running on Tomcat 7.0.56. However, when I try it with Struts 2 framework, it does not work.
Some researches that I did suggested that it could have been because of the way Struts 2 maps the URL, but to no avail, I am still unable to communicate with the Websocket endpoint on my server.
Do anyone have any idea how to implement Websocket with Struts 2 framework?
The code that I used for the websocket is as follow:
#ServerEndpoint("/mssendpoint")
public class MSSEndpoint {
public static Logger logger = Logger.getLogger(MSSEndpoint.class);
/* Queue for all open WebSocket sessions */
static Queue<Session> queue = new ConcurrentLinkedQueue<Session>();
static Set<WebsocketListener> listeners = new HashSet<WebsocketListener>();
public static void send(String msg) {
try {
/* Send updates to all open WebSocket sessions */
for (Session session : queue) {
session.getBasicRemote().sendText(msg);
logger.info("Sent: " + msg);
}
}
catch (IOException e) {
logger.error(e.toString());
}
}
#OnOpen
public void openConnection(Session session) {
/* Register this connection in the queue */
queue.add(session);
logger.info("Connection opened.");
}
#OnClose
public void closedConnection(Session session) {
/* Remove this connection from the queue */
queue.remove(session);
logger.info("Connection closed.");
}
#OnError
public void error(Session session, Throwable t) {
/* Remove this connection from the queue */
queue.remove(session);
logger.info(t.toString());
logger.info("Connection error.");
}
#OnMessage
public void onMessage(String message, Session session) {
if (queue.contains(session)) {
notifyListener(message);
}
}
public static void addListener(WebsocketListener listener){
listeners.add(listener);
}
public static void removeListener(WebsocketListener listener){
listeners.remove(listener);
}
public void notifyListener(String message){
for (WebsocketListener listener : listeners) {
listener.onMessage(message);
}
}
}
I have used this exact same code on normal Java Servlet application running on Tomcat 7.0.56 and with a client, I could connect to it.
I used 'Simple Websocket Client' chrome extension as the client.
All I need was to connect to ws://localhost/myprojectname/mssendpoint and it will connect directly.
EDIT2:
I forgot to mention that the error was that when I tried to connect, it will simply say undefined when I use the Websocket Client. Assuming that my Struts 2 project is called cms, by right I should just need to access ws://localhost/myprojectname/mssendpoint. But then it produces that undefined message.
Related
I have created an inbound handler of type SimpleChannelInboundHandler and added to pipeline. My intention is every time a connection is established, I wanted to send an application message called session open message and make the connection ready to send the actual message. To achieve this, the above inbound handler
over rides channelActive() where session open message is sent, In response to that I would get a session open confirmation message. Only after that I should be able to send any number of actual business message. I am using FixedChannelPool and initialised as follows. This works well some time on startup. But if the remote host closes the connection, after that if a message is sent calling the below sendMessage(), the message is sent even before the session open message through channelActive() and its response is obtained. So the server ignores the message as the session is not open yet when the business message was sent.
What I am looking for is, the pool should return only those channel that has called channelActive() event which has already sent the session open message and it has got its session open confirmation message from the server. How to deal with this situation?
public class SessionHandler extends SimpleChannelInboundHandler<byte[]> {
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
if (ctx.channel().isWritable()) {
ctx.channel().writeAndFlush("open session message".getBytes()).;
}
}
}
// At the time of loading the applicaiton
public void init() {
final Bootstrap bootStrap = new Bootstrap();
bootStrap.group(group).channel(NioSocketChannel.class).remoteAddress(hostname, port);
fixedPool = new FixedChannelPool(bootStrap, getChannelHandler(), 5);
// This is done to intialise connection and the channelActive() from above handler is invoked to keep the session open on startup
for (int i = 0; i < config.getMaxConnections(); i++) {
fixedPool.acquire().addListener(new FutureListener<Channel>() {
#Override
public void operationComplete(Future<Channel> future) throws Exception {
if (future.isSuccess()) {
} else {
LOGGER.error(" Channel initialzation failed...>>", future.cause());
}
}
});
}
}
//To actually send the message following method is invoked by the application.
public void sendMessage(final String businessMessage) {
fixedPool.acquire().addListener(new FutureListener<Channel>() {
#Override
public void operationComplete(Future<Channel> future) throws Exception {
if (future.isSuccess()) {
Channel channel = future.get();
if (channel.isOpen() && channel.isActive() && channel.isWritable()) {
channel.writeAndFlush(businessMessage).addListener(new GenericFutureListener<ChannelFuture>() {
#Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
// success msg
} else {
// failure msg
}
}
});
fixedPool.release(channel);
}
} else {
// Failure
}
}
});
}
If there is no specific reason that you need to use a FixedChannelPool then you can use another data structure (List/Map) to store the Channels. You can add a channel to the data structure after sending open session message and remove it in the channelInactive method.
If you need to perform bulk operations on channels you can use a ChannelGroup for the purpose.
If you still want you use the FixedChannelPool you may set an attribute in the channel on whether open message was sent:
ctx.channel().attr(OPEN_MESSAGE_SENT).set(true);
you can get the attribute as follows in your sendMessage function:
boolean sent = ctx.channel().attr(OPEN_MESSAGE_SENT).get();
and in the channelInactive you may set the same to false or remove it.
Note OPEN_MESSAGE_SENT is an AttributeKey:
public static final AttributeKey<Boolean> OPEN_MESSAGE_SENT = AttributeKey.valueOf("OPEN_MESSAGE_SENT");
I know this is a rather old question, but I stumbled across the similar issue, not quite the same, but my issue was the ChannelInitializer in the Bootstrap.handler was never called.
The solution was to add the pipeline handlers to the pool handler's channelCreated method.
Here is my pool definition code that works now:
pool = new FixedChannelPool(httpBootstrap, new ChannelPoolHandler() {
#Override
public void channelCreated(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(HTTP_CODEC, new HttpClientCodec());
pipeline.addLast(HTTP_HANDLER, new NettyHttpClientHandler());
}
#Override
public void channelAcquired(Channel ch) {
// NOOP
}
#Override
public void channelReleased(Channel ch) {
// NOOP
}
}, 10);
So in the getChannelHandler() method I assume you're creating a ChannelPoolHandler in its channelCreated method you could send your session message (ch.writeAndFlush("open session message".getBytes());) assuming you only need to send the session message once when a connection is created, else you if you need to send the session message every time you could add it to the channelAcquired method.
Here is my scenario.
The grpc server is an asynchronous sever which subscribes data from other source.
Also it provides a subscribe function to its client, thus it could push the data to the grpc client once it receives data from other source. The server is implemented in Java.
#Override
public StreamObserver<Message> subscribe (
StreamObserver<Message> responseObserver) {
return new StreamObserver<Message>() {
#Override
public void onNext(Message message) {
api.subscribe(message.value, Message -> {
synchronized (responseObserver) {
...
...
// get data from other source
responseObserver.onNext(Converter.create().toProtobuf(Message.class, data));
}
});
}
#Override
public void onError(Throwable t) {
log.warn("Encountered error in sub", t);
}
#Override
public void onCompleted() {
responseObserver.onCompleted();
}
};
}
I wanna use python to implement a grpc client to subscribe from this server. However, it appears weird once python subscribe data, it immediately shutdown without waiting for the asynchronous return from Java server. However, the Java client could run forever and waiting for the asynchronous data from the server.
Proto
message Message{
string value = 1;
}
service test {
rpc subscribe(stream Message) returns (stream Message) {}
}
The Python client Code(not working)
def gen_message():
yield test.Message(value="2")
def run():
channel = grpc.insecure_channel('localhost:50051')
stub = test_grpc.MessengerStub(channel)
stream = stub.subscribe(gen_message())
try:
for e in stream:
print(e)
except grpc._channel._Rendezvous as err:
print(err)
run()
The Java Code(working)
StreamObserver<Message> requestObserver = stub.subscribe(new StreamObserver<Message>() {
#Override
public void onNext(Message message) {
System.out.println(message)
}
#Override
public void onError(Throwable t) {
t.printStackTrace();
}
#Override
public void onCompleted() {
}
});
Message message = Message.newBuilder().build();
requestObserver.onNext(message);
I have been got confused. How to implement the same feature in python client?
Thanks~
p.s. If the server is an while True server other than asynchronous server, the python client works. I suspects that the python client does not know the "asynchronous" server and once its stream has no new data it close the connection.
I'm working on my first WebSocket app, and was surprised when code like this did not work consistently:
#ServerEndpoint(value="/msg/{owner}", encoders=MessageEncoder.class, decoders=MessageEncoder.class)
public class WebSocketListener {
public WebSocketListener() {
}
#OnOpen
public void open(Session session, #PathParam("owner") String owner) {
if (session.getUserPrincipal() != null) {
session.getUserProperties().put("owner", owner);
}
else {
try {
session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "Not authorized"));
} catch (IOException e) {
}
}
}
#OnClose
public void close(Session session) {
}
#OnError
public void onError(Throwable error) {
}
#OnMessage
public void onMessage(final Session session, final Message message) {
String owner = (String)session.getUserProperties().get("owner");
for (Session s:session.getOpenSessions() {
System.out.println(s);
if (s.isOpen() && owner.equals(s.getUserProperties().get("owner"))) {
s.getAsyncRemote().sendObject(message);
}
}
}
}
What I observe when connecting to this end point from two clients and the same 'owner', is that sometimes both sessions are returned from getOpenSession(), but more often than not only that user's session is returned, as evidenced by the System.out.println. My workaround was to ditch using getOpenSessions by adding a static map to this class, adding the session to a list using the owner as the key, and just using that instead. But is this a known bug, I couldn't find anyone complaining about this from searching?
it seems to be a bug in tomcat, I faced the same issue with embedded tomcat in Spring Boot, changing the server to Jetty resolved the issue. In Spring Docs, there is a section explaining how to switch the server: http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-use-jetty-instead-of-tomcat
I have a java client calling a java SOAP webservice deployed on glassfish server 4.1, that same java client also has a websocket connection to a server deployed on the same server.
Basically my architecture is this I have a java client calling a SOAP webservice, and connected to a java websocket server endpoint. Why? You can akin this to a bank, the client calls the SOAP webservice for say to check the account balance, then the websocket serves in notifying the client when a credit/debit has been made on the account.
Problem: In my main method I startup connection with the websocket, after that I call the webservice for a resource. Now the problem is I have to try several times before a call to the webservice is made. Its a very strange bug. I tried initializing the websocket on a different thread but I still get the same problem. Please see the code below
Bank webservice:
//relevant import statements
#WebService(serviceName = "BankService")
public class BankService {
private static final Map<Integer, AccountHolder> accountHolders = new ConcurrentHashMap<Integer, AccountHolder>();
private static AccountHolder customer, merchant;
public BankService(){
customer = new AccountHolder("Customer 1", 1234, 4321, 4000, null, null);
merchant = new AccountHolder("Merchant", 5678, 8765, 1000, null, null);
accountHolders.put(1234, customer);
accountHolders.put(5678, merchant);
}
#WebMethod(operationName = "getBalance")
public String getBalance(#WebParam(name = "acctNum") int acctNum) {
AccountHolder client;
synchronized(accountHolders){
client = accountHolders.get(acctNum);
}
return client.toString(); //returns a string containing balance
}
}
Bank notification websocket:
#ServerEndpoint(value="/n")
public class NotificationServer {
private Logger logger = Logger.getLogger(this.getClass().getName());
#OnOpen
public void onOpen(Session session) throws InterruptedException, IOException {
logger.info("Connected ... " + session.getId());
}
#OnMessage
public void notifyCustomer(String message, Session session) throws InterruptedException, IOException {
session.getBasicRemote().sendText(message);
}
#OnClose....
}
Client
public class Client {
static Client client;
private String getBalance(int acctNum) {
BankService_Service service = new BankWebService.BankService_Service();
BankService port = service.getBankServicePort();
return port.getBalance(acctNum);
}
private void wsc() throws InterruptedException {
BankNotificationClient bnc = new BankNotificationClient();
Thread thread = new Thread(bnc);
thread.start();
}
public static void main(String[] args) throws InterruptedException {
client = new Client();
client.wsc();
Scanner scanner = new Scanner(System.in);
while(!"quit".equals(scanner.next())){
if(scanner.next().equals("check")){
System.out.println(client.getBalance(1234));
}
}
}
}
Bank notification client:
//necessary import statements
import javax.*;
import org.glassfish.tyrus.client.ClientManager;
#ClientEndpoint
public class BankNotificationClient implements Runnable {
private static Session clientSession;
private Logger logger = Logger.getLogger(this.getClass().getName());
ClientManager clientws;
#OnOpen
public void onOpen(Session session) throws InterruptedException {
logger.info("Connected ... " + session.getId());
clientSession = session;
}
#OnMessage
public void onMessage(String message, Session session) {
....
}
#OnClose
public void onClose(Session session, CloseReason closeReason) {
logger.info(String.format("Session %s has closed because %s", session.getId(), closeReason));
}
public void run() {
try {
client = ClientManager.createClient();
client.connectToServer(BankNotificationClient.class, new URI("ws://localhost:8080/BankService/n"));
System.out.println("working" + client.getClass());
} catch (DeploymentException | URISyntaxException e) {
logger.info("Something has gone wrong initializing the socket");
}
}
}
I am using netbeans 8.0.2 as my development IDE. Having deployed the webservice and websocket to glassfish server 4.1. Running this would give the following output
Output:
Aug 05, 2015 11:31:18 PM BankNotificationHandler.BankNotificationClient onOpen
INFO: Connected ... 981de7b4-91b9-43b6-beaf-c219500ee77a
check
check
Dear Customer Customer 1, your account balance is 4000.
check
check
Dear Customer Customer 1, your account balance is 4000.
check
check
Dear Customer Customer 1, your account balance is 4000.
As you can see from the output I have to type and enter "check" twice before I get a result from the webservice. Using a debugger on the first entry of check the code suddenly halts at the if statement in the while loop, then I have to enter "check" again. On entering check it continues from where it stopped in the if statement line and proceeds to give me the desired result.
Please let me know if there is any other information I have left out.
Cheers.
I use websockets and Glassfish. I call start() funcion on load page. When start function contains alert it sends the message to the server but when I don't put the alert it doesn't work. I can't figure out why.
java script
function start() {
alert('a'); //this alert
webSocket.send('start_server');
}
function onMessage(event) {
document.getElementById('messages').innerHTML
+= event.data;
}
server side
#OnMessage
public void onMessage(String message, Session session)
throws IOException, InterruptedException {
System.out.println("Message recieved");
session.getBasicRemote().sendText(message);
}
#OnOpen
public void onOpen() {
System.out.println("Client connected");
}
Where do you initialize the web-socket on the client side?
The alert suspends execution until you click ok - so i guess your order of execution is wrong (initialize the websocket before webSocket.send)
You may send data after the WebSocket#open event happend.
var connection = new WebSocket(...)
connection.onopen = function () {
connection.send('Ping');
};
Source: html5rocks