I have a test which should connect and subscribe multiple users to a websocket endpoint. The test connects the user but doesn't subscribe the users.
This is how I try to connect the users:
List<ConsumerStompSessionHandler> consumers = new ArrayList<>();
for (int i = 0; i < NUMBER_OF_USERS; i++) {
consumers.add(new ConsumerStompSessionHandler(BROADCAST_MESSAGE_COUNT, connectLatch, subscribeLatch,
messageLatch, disconnectLatch, failure));
stompClient.connect(stompUrl, consumers.get(i), host, port);
}
if (failure.get() != null) {
throw new AssertionError("Test failed", failure.get());
}
if (!connectLatch.await(5000, TimeUnit.MILLISECONDS)) {
fail("Not all users connected, remaining: " + connectLatch.getCount());
}
if (!subscribeLatch.await(5000, TimeUnit.MILLISECONDS)) {
fail("Not all users subscribed, remaining: " +
subscribeLatch.getCount());
}
stopWatch.stop();
logger.debug("Finished: " + stopWatch.getLastTaskTimeMillis() + " millis");
The afterConnect method, which I think it is not executed:
#Override
public void afterConnected(final StompSession session, StompHeaders connectedHeaders) {
this.connectLatch.countDown();
session.setAutoReceipt(true);
session.subscribe("/topic/greetings", new StompFrameHandler() {
public Type getPayloadType(StompHeaders headers) {
return String.class;
}
public void handleFrame(StompHeaders headers, Object payload) {
if (messageCount.incrementAndGet() == expectedMessageCount) {
messageLatch.countDown();
disconnectLatch.countDown();
session.disconnect();
}
}
}).addReceiptTask(new Runnable() {
#Override
public void run() {
subscribeLatch.countDown();
}
});
}
I also tried to put a Thread.sleep(5000) in the for after the connection but it didn't work.
Can anyone tell me why it doesn't subscribe.
The subscribe url is the same from #Controller -> #SendTo("/topic/greetings").
Related
I want to send multiple API requests, so I'm using RxJava's Zip operator, I want to know the success rate of the API requests to show it to the user, but here, whenever one request getting failed, I couldn't see any logs inside the complete method,
How to listen over all the responses together (success/fail) and find the success rate?
List<io.reactivex.rxjava3.core.Observable<Object>> requests = new ArrayList<>();
requests.add(
RetrofitInstance.getRetrofitClient()
.create(IService.class)
.sendMessage("123", com)); // my custom model class
Observable
.zip(requests, // list of API requests
new Function<Object[], List<Object>>() {
#Override
public List<Object> apply(Object[] objects) throws Exception {
Log.d("onSubscribe", "apply: " + objects.length);
for (Object o : objects) {
Log.d(TAG, "apply: %%%%% " + o.toString());
messageResponse.add((Object) o);
}
if (messageResponse.size() == requests.size()) {
Log.d(TAG, "apply: req size " + requests.size());
}
Log.d(TAG, "apply: ##4");
msgResponse[0] = true;
return messageResponse;
}
})
.subscribeOn(Schedulers.io())
.subscribe(new Observer<List<Object>>() {
#Override
public void onSubscribe(
#io.reactivex.rxjava3.annotations.NonNull Disposable d) {
Log.d(TAG, "onSubscribe: ");
}
#Override
public void onNext(
#io.reactivex.rxjava3.annotations.NonNull List<Object> objects) {
Log.d(TAG, "onNext: ");
}
#Override
public void onError(
#io.reactivex.rxjava3.annotations.NonNull Throwable e) {
Log.d(TAG, "onError: ");
}
#Override
public void onComplete() {
Log.d(TAG, "onComplete: ");
}
});
I am migrating an enterprise-level application based on JBOSS EAP 7.3 using the WebSocket Servlets to IBM WebSphere Application Server 9.0.5.6 (WAS). The problem is that when I try to connecting to WebSocket endpoint it does nothing. In WebSphere, I have configured all virtual hosts and ports accordingly and my WebSocket endpoint class looks like below.
#ServerEndpoint("/WebSocketServices")
public class ClientConnectorWebSocketCore {
private static final OMSLogHandlerI logger = new Log4j2HndlAdaptor("ClientConnectorWebSocketCore");
private ClientConnectorFacadeWrapperRemote clientConnectorFacadeRemote;
private EJBServiceLocatorI ejbServiceLocatorI;
#OnOpen
public void onConnectionOpen(Session session) {
session.setMaxIdleTimeout(120000); //todo //milli seconds
getEjbServiceLocatorI();
logger.elkLog("29", LogEventsEnum.WSOCKET_SESSION_CONNECT, " Session Create:" + session.getId());
}
#OnMessage
public String onMessage(String message) {
return handleJSONRequest(message);
}
#OnClose
public void onConnectionClose(Session session) {
logger.elkLog("42", LogEventsEnum.WSOCKET_SESSION_CLOSE, " Session Close:" + session.getId());
}
#OnError
public void onConnectionError(Session session, Throwable t) {
// logger.info("LN:47", session.getId(), LogEventsEnum.WEB_SOCKET_ONERROR, "WebSocket OnException" + t.getMessage());
logger.elkLog("48", LogEventsEnum.WEB_SOCKET_ONERROR, " Session error:" + session.getId() + ", Msg:" + t.getMessage());
}
public void msgBroadCast(Session session, String msg) {
for (Session session1 : session.getOpenSessions()) {
if (session1.isOpen()) {
session1.getAsyncRemote().sendText(msg);
}
}
}
private EJBServiceLocatorI getEjbServiceLocatorI(){
if (ejbServiceLocatorI == null){
ejbServiceLocatorI =
(EJBServiceLocatorI) SpringBeanFactoryLoader.getInstance().getBeanLoader().getBean(EJBServiceLocatorI.class);
}
return ejbServiceLocatorI;
}
private ClientConnectorFacadeWrapperRemote getClientConnectFacade() {
if (clientConnectorFacadeRemote == null) {
try {
ejbServiceLocatorI = getEjbServiceLocatorI();
clientConnectorFacadeRemote =
(ClientConnectorFacadeWrapperRemote) ejbServiceLocatorI.contextLookupConnectorFacadeRemote(ClientConnectorFacadeWrapperRemote.class);
} catch (Exception e) {
logger.error("LN:66", "Error in Creating Client connector " + e.getMessage(), e);
}
}
return clientConnectorFacadeRemote;
}
private String handleJSONRequest(String jsonRequest) {
ClientConnectorFacadeWrapperRemote clientConnector = getClientConnectFacade();
String response = null;
if (clientConnector != null) {
response = clientConnector.processMessage(jsonRequest);
}
return response;
}
#OnMessage
public void pongMessage(Session session, PongMessage msg) {
msg.getApplicationData().toString();
}
public void setEjbServiceLocatorI(EJBServiceLocatorI ejbServiceLocatorI) {
this.ejbServiceLocatorI = ejbServiceLocatorI;
}
}
Any advice is highly appreciated.
If your requests flow through a web server with WebSphere plugin, you need to ensure plugin is at level 9.0.5.6 or higher due to bug (PH27966) which can block websocket traffic.
I have the following class. I'm trying to have the WebSocket reconnect in case of failure
public class WebSocketClient extends WebSocketListener {
volatile OkHttpClient client;
volatile WebSocket webSocket;
volatile Boolean isConnected = false;
public WebSocketClient() {
Proxy proxy = null;
if (Main.useProxy) {
tinder.CustomProxy proxyCustom = ProxyManager.GetStaticProxy(ThreadLocalManager.account.get().getProxyId());
proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress(proxyCustom.getProxyIp(), proxyCustom.getProxyPort()));
}
client = new OkHttpClient.Builder().proxy(proxy).readTimeout(2, TimeUnit.SECONDS)
.connectTimeout(2, TimeUnit.SECONDS).build();
Request request = new Request.Builder().url("wss://echo.com/ws")
.addHeader("Accept-Language", "en").build();
webSocket = client.newWebSocket(request, this);
}
#Override
public void onOpen(WebSocket webSocket, Response response) {
AnsiConsole.out.println(Ansi.ansi().fg(Ansi.Color.GREEN).a("Socket connection successful").reset());
isConnected = true;
}
#Override
public void onMessage(WebSocket webSocket, String text) {
System.out.println("Text MESSAGE: " + text);
}
#Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
}
#Override
public void onClosing(WebSocket webSocket, int code, String reason) {
webSocket.close(1000, null);
System.out.println("CLOSE: " + code + " " + reason);
isConnected = false;
}
#Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
isConnected = false;
AnsiConsole.out
.println(Ansi.ansi().fg(Ansi.Color.RED).a("Socket connection failed! will try to reconnect").reset());
while (!isConnected) {
try {
AnsiConsole.out
.println(Ansi.ansi().fg(Ansi.Color.YELLOW).a("Waiting to try socket connection!").reset());
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Request request = new Request.Builder().url("wss://echo.com/ws")
.addHeader("Accept-Language", "en").build();
webSocket = client.newWebSocket(request, this);
}
if (isConnected) {
AnsiConsole.out.println(Ansi.ansi().fg(Ansi.Color.GREEN).a("Socket connection successful").reset());
}
}
public void close() {
if (webSocket != null) {
webSocket.close(1000, "Connection closed");
}
client.dispatcher().executorService().shutdown();
}
}
The problem is if it takes a few attempts to reconnect, then the onFailure method will get called multiple times. Causing multiple web socket connections instead of one.
How can I have the single connection reconnect when the websocket disconnects?
public class WebSocketClient extends WebSocketListener {
volatile OkHttpClient client;
volatile WebSocket webSocket;
volatile Boolean isConnected = false;
public WebSocketClient() {
Proxy proxy = null;
if (Main.useProxy) {
tinder.CustomProxy proxyCustom = ProxyManager.GetStaticProxy(ThreadLocalManager.account.get().getProxyId());
proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress(proxyCustom.getProxyIp(), proxyCustom.getProxyPort()));
}
client = new OkHttpClient.Builder().proxy(proxy).readTimeout(2, TimeUnit.SECONDS)
.connectTimeout(2, TimeUnit.SECONDS).build();
Request request = new Request.Builder().url("wss://echo.com/ws")
.addHeader("Accept-Language", "en").build();
webSocket = client.newWebSocket(request, this);
// First Change
client.connectionPool.evictAll();
}
#Override
public void onOpen(WebSocket webSocket, Response response) {
AnsiConsole.out.println(Ansi.ansi().fg(Ansi.Color.GREEN).a("Socket connection successful").reset());
isConnected = true;
}
#Override
public void onMessage(WebSocket webSocket, String text) {
System.out.println("Text MESSAGE: " + text);
}
#Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
}
#Override
public void onClosing(WebSocket webSocket, int code, String reason) {
webSocket.close(1000, null);
System.out.println("CLOSE: " + code + " " + reason);
isConnected = false;
}
#Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
// Second Change
webSocket.close(1000, null);
close();
Thread.sleep(10000);
Request request = new Request.Builder().url("wss://echo.com/ws")
.addHeader("Accept-Language", "en").build();
webSocket = client.newWebSocket(request, this);
}
public void close() {
if (webSocket != null) {
webSocket.close(1000, "Connection closed");
}
}
}
For multiple idle connection client provide a connectionPool
client.connectionPool().evictAll();
the evictAll() method evicts all the connections.
I am trying to communicate with an external TCP server using TcpOutboundGateway and a client TcpConnectionFactory. In my scenario, each connection should be associated with different thread (each connection on the thread might be used for more then one request/response).
So I used a ThreadAffinityClientConnectionFactory from this topic: Spring Integration tcp client multiple connections
It worked fine until I tried to open more than 4 concurrent connections, the fifth (and over) connection is failing on timeout.
I figured out that org.springframework.integration.ip.tcp.TcpOutboundGateway uses semaphore in handleRequestMessage method to acquire a connection, so I overridden TcpOuboundGateway like this:
public class NoSemaphoreTcpOutboundGateway extends TcpOutboundGateway {
private volatile AbstractClientConnectionFactory connectionFactory;
private final Map<String, NoSemaphoreTcpOutboundGateway.AsyncReply> pendingReplies = new ConcurrentHashMap();
#Override
public boolean onMessage(Message<?> message) {
String connectionId = (String)message.getHeaders().get("ip_connectionId");
if(connectionId == null) {
this.logger.error("Cannot correlate response - no connection id");
this.publishNoConnectionEvent(message, (String)null, "Cannot correlate response - no connection id");
return false;
}
if(this.logger.isTraceEnabled()) {
this.logger.trace("onMessage: " + connectionId + "(" + message + ")");
}
NoSemaphoreTcpOutboundGateway.AsyncReply reply = (NoSemaphoreTcpOutboundGateway.AsyncReply)this.pendingReplies.get(connectionId);
if(reply == null) {
if(message instanceof ErrorMessage) {
return false;
} else {
String errorMessage = "Cannot correlate response - no pending reply for " + connectionId;
this.logger.error(errorMessage);
this.publishNoConnectionEvent(message, connectionId, errorMessage);
return false;
}
} else {
reply.setReply(message);
return false;
}
}
#Override
protected Message handleRequestMessage(Message<?> requestMessage) {
connectionFactory = (AbstractClientConnectionFactory) this.getConnectionFactory();
Assert.notNull(this.getConnectionFactory(), this.getClass().getName() + " requires a client connection factory");
TcpConnection connection = null;
String connectionId = null;
Message var7;
try {
/*if(!this.isSingleUse()) {
this.logger.debug("trying semaphore");
if(!this.semaphore.tryAcquire(this.requestTimeout, TimeUnit.MILLISECONDS)) {
throw new MessageTimeoutException(requestMessage, "Timed out waiting for connection");
}
haveSemaphore = true;
if(this.logger.isDebugEnabled()) {
this.logger.debug("got semaphore");
}
}*/
connection = this.getConnectionFactory().getConnection();
NoSemaphoreTcpOutboundGateway.AsyncReply e = new NoSemaphoreTcpOutboundGateway.AsyncReply(10000);
connectionId = connection.getConnectionId();
this.pendingReplies.put(connectionId, e);
if(this.logger.isDebugEnabled()) {
this.logger.debug("Added pending reply " + connectionId);
}
connection.send(requestMessage);
//connection may be closed after send (in interceptor) if its disconnect message
if (!connection.isOpen())
return null;
Message replyMessage = e.getReply();
if(replyMessage == null) {
if(this.logger.isDebugEnabled()) {
this.logger.debug("Remote Timeout on " + connectionId);
}
this.connectionFactory.forceClose(connection);
throw new MessageTimeoutException(requestMessage, "Timed out waiting for response");
}
if(this.logger.isDebugEnabled()) {
this.logger.debug("Response " + replyMessage);
}
var7 = replyMessage;
} catch (Exception var11) {
this.logger.error("Tcp Gateway exception", var11);
if(var11 instanceof MessagingException) {
throw (MessagingException)var11;
}
throw new MessagingException("Failed to send or receive", var11);
} finally {
if(connectionId != null) {
this.pendingReplies.remove(connectionId);
if(this.logger.isDebugEnabled()) {
this.logger.debug("Removed pending reply " + connectionId);
}
}
}
return var7;
}
private void publishNoConnectionEvent(Message<?> message, String connectionId, String errorMessage) {
ApplicationEventPublisher applicationEventPublisher = this.connectionFactory.getApplicationEventPublisher();
if(applicationEventPublisher != null) {
applicationEventPublisher.publishEvent(new TcpConnectionFailedCorrelationEvent(this, connectionId, new MessagingException(message, errorMessage)));
}
}
private final class AsyncReply {
private final CountDownLatch latch;
private final CountDownLatch secondChanceLatch;
private final long remoteTimeout;
private volatile Message<?> reply;
private AsyncReply(long remoteTimeout) {
this.latch = new CountDownLatch(1);
this.secondChanceLatch = new CountDownLatch(1);
this.remoteTimeout = remoteTimeout;
}
public Message<?> getReply() throws Exception {
try {
if(!this.latch.await(this.remoteTimeout, TimeUnit.MILLISECONDS)) {
return null;
}
} catch (InterruptedException var2) {
Thread.currentThread().interrupt();
}
for(boolean waitForMessageAfterError = true; this.reply instanceof ErrorMessage; waitForMessageAfterError = false) {
if(!waitForMessageAfterError) {
if(this.reply.getPayload() instanceof MessagingException) {
throw (MessagingException)this.reply.getPayload();
}
throw new MessagingException("Exception while awaiting reply", (Throwable)this.reply.getPayload());
}
NoSemaphoreTcpOutboundGateway.this.logger.debug("second chance");
this.secondChanceLatch.await(2L, TimeUnit.SECONDS);
}
return this.reply;
}
public void setReply(Message<?> reply) {
if(this.reply == null) {
this.reply = reply;
this.latch.countDown();
} else if(this.reply instanceof ErrorMessage) {
this.reply = reply;
this.secondChanceLatch.countDown();
}
}
}
}
the configurations of SpringContext looks like this:
#Configuration
#ImportResource("gateway.xml")
public class Conf {
#Bean
#Autowired
#ServiceActivator(inputChannel = "clientOutChannel")
public NoSemaphoreTcpOutboundGateway noSemaphoreTcpOutboundGateway(ThreadAffinityClientConnectionFactory cf, DirectChannel clientInChannel){
NoSemaphoreTcpOutboundGateway gw = new NoSemaphoreTcpOutboundGateway();
gw.setConnectionFactory(cf);
gw.setReplyChannel(clientInChannel);
gw.setRequestTimeout(10000);
return gw;
}
<int-ip:tcp-connection-factory
id="delegateCF"
type="client"
host="${remoteService.host}"
port="${remoteService.port}"
single-use="true"
lookup-host="false"
ssl-context-support="sslContext"
deserializer="clientDeserializer"
serializer="clientSerializer"
interceptor-factory-chain="clientLoggingTcpConnectionInterceptorFactory"
using-nio="false"/>
The delegateCF is passed to ThreadAffinityClientConnectionFactory constructor
So, the question is:
Is it OK to use NoSemaphoreTcpOutboundGateway in conjunction with ThreadAffinityClientConnectionFactory in terms of concurrency?
Looks like you go right way, but at the same time I think you don't need custom TcpOutboundGateway. The semaphore logic is based on the:
if (!this.isSingleUse) {
logger.debug("trying semaphore");
if (!this.semaphore.tryAcquire(this.requestTimeout, TimeUnit.MILLISECONDS)) {
throw new MessageTimeoutException(requestMessage, "Timed out waiting for connection");
}
at the same time look at Gary's solution for the ThreadAffinityClientConnectionFactory:
#Bean
public TcpNetClientConnectionFactory delegateCF() {
TcpNetClientConnectionFactory clientCF = new TcpNetClientConnectionFactory("localhost", 1234);
clientCF.setSingleUse(true); // so each thread gets his own connection
return clientCF;
}
#Bean
public ThreadAffinityClientConnectionFactory affinityCF() {
return new ThreadAffinityClientConnectionFactory(delegateCF());
}
Pay attention to the comment. Only you need is delegate isSingleUse().
I currently have a problem while working with Mina. I am able to create a NIOAcceptor and Connector and connect the client to the server. Upon session being created in the Server, it sends the handshake packet which in turn validates and sends back validation to the server to see if files are up-to-date, etc. The server receives this validation and correctly deciphers the packet and sends the packet to the Client to display the game window. However, after this initial connection, I can no longer send packets to the server via the Client.
ServerHandler:
#Override
public void sessionOpened(IoSession session) {
log.info("[Login] to [" + GameConstants.GAME_NAME + ": IoSession with {} opened", session.getRemoteAddress());
Client c = new Client(session);
connectedClients.add(session.getRemoteAddress().toString());
session.setAttribute(Client.KEY, c);
c.write(PacketCreator.getHandshake());
// c.write(PacketCreator.getPing());
}
#Override
public void messageReceived(IoSession session, Object message) {
PacketReader reader = new PacketReader((byte[]) message);
Client c = (Client) session.getAttribute(Client.KEY);
short header = reader.readShort();
PacketHandler handler = PacketProcessor.getHandler(header);
System.out.println("Received opcode: 0x" + Integer.toHexString(header).toUpperCase());
if (handler != null) {
handler.handlePacket(reader, c);
} else {
log.info("Received opcode: 0x" + Integer.toHexString(header).toUpperCase() + " with no handler.");
}
}
#Override
public void exceptionCaught(IoSession session, Throwable cause) {
System.out.println("session error");
}
#Override
public void sessionClosed(IoSession session) throws Exception {
System.out.println("Session closing: " + session.getRemoteAddress().toString());
connectedClients.remove(session.getRemoteAddress().toString());
Client c = (Client) session.getAttribute(Client.KEY);
if (c != null) {
c.disconnect();
c.dispose();
} else {
log.warn("Client is null in sessionClosed for ip {} when it shouldn't be", session.getRemoteAddress());
}
super.sessionClosed(session);
}
ClientHandler:
#Override
public void sessionOpened(IoSession session) {
System.out.println("Session opened: " + session);
Server s = new Server(session);
session.setAttribute(Server.KEY, s);
s.write(PacketCreator.getPong());
}
#Override
public void messageReceived(IoSession session, Object message) {
PacketReader reader = new PacketReader((byte[]) message);
Server s = (Server) session.getAttribute(Server.KEY);
short header = reader.readShort();
PacketHandler handler = PacketProcessor.getHandler(header);
if (handler != null) {
handler.handlePacket(reader, s);
} else {
log.info("Received opcode: 0x" + Integer.toHexString(header).toUpperCase() + " with no handler.");
}
}
#Override
public void exceptionCaught(IoSession session, Throwable cause) {
System.out.println("session error");
log.error("Exception caught in Server Handler: ", cause);
}
#Override
public void sessionClosed(IoSession session) throws Exception {
// TODO
System.out.println("session closed");
super.sessionClosed(session);
}
Client (NIOConnection class):
public static void connectToServer() throws Throwable {
NioSocketConnector connector = new NioSocketConnector();
connector.setConnectTimeoutMillis(1000 * 30); // 30 seconds
connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
connector.setHandler(new ClientHandler());
IoSession session;
long startTime = System.currentTimeMillis();
for (;;) {
try {
ConnectFuture future = connector.connect(new InetSocketAddress("127.0.0.1", 9494)); // 24.7.142.74
future.awaitUninterruptibly();
session = future.getSession();
break;
} catch (RuntimeIoException e) {
log.error("Failed to connect", e);
Thread.sleep(5000);
}
}
session.getCloseFuture().awaitUninterruptibly();
}
Server (NIOAcceptor class):
private static void initializeLoginServer() {
PacketProcessor.registerHandlers();
acceptor = new NioSocketAcceptor();
// acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));// TODO: encoding/decoding packets
acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
acceptor.getSessionConfig().setReadBufferSize(2048);
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
acceptor.getSessionConfig().setTcpNoDelay(true);
acceptor.setHandler(new ServerHandler(1));
try {
acceptor.bind(new InetSocketAddress(GameConstants.SERVER_PORT));
} catch (Exception e) {
log.error("Could not bind. ", e);
}
log.info("Login Server: Listening on port " + GameConstants.SERVER_PORT);
}