I am totally new in XMPP and Spring Integration, and want to send message to FCM user. I created an XML configuration for Outbound Message as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-xmpp="http://www.springframework.org/schema/integration/xmpp"
...>
<context:component-scan base-package="com.avantovr.esysync_backend.webservices.restful.fcm.xmppserver" />
<int-xmpp:outbound-channel-adapter id="xmppOutboundAdapter" channel="xmppOutboundChannel" xmpp-connection="xmppConnection"/>
<int:service-activator ref="fcmSender" input-channel="xmppOutbound" />
<int:logging-channel-adapter id="xmppOutboundChannel" log-full-message="true" />
</beans>
Now I want to create a Java Class in which there is method for sending Downstrwam messagr via XMPP to FCM. Pls is there any beginner example for sending and receiving xmpp messages to FCM via Spring integration?
This may not be the best solution but its good enough for me. First time dealing with XMPP.
I dont have any XML configurations other than pom.xml dependencies.
There is a 5 seconds sleep and reconnect after a disconnect. FCM disconnects XMPP connection from time to time to load balance.
Sending ACKs back is working, receiveing ACKs and NACKs are not tested.
To send a message from java -> fcm -> phone;
#Autowired
private XmppConfig xmppConfig;
xmppConfig.getXmppConnection().sendStanza(generateStanza(messageId,json));
json above shoulde be like this, from https://firebase.google.com/docs/cloud-messaging/server
{
"to":"REGISTRATION_ID", // "to" replaces "registration_ids"
"message_id":"m-1366082849205" // new required field
"data":
{
"hello":"world",
}
"time_to_live":"600",
"delivery_receipt_requested": true/false
}
smack dependencies
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-tcp</artifactId>
<version>4.3.4</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-java7</artifactId>
<version>4.3.4</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-extensions</artifactId>
<version>4.3.4</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20190722</version>
</dependency>
application.properties
fcm.sender_id=YOUR_SENDER_ID#fcm.googleapis.com
fcm.server_key=YOUR_SERVER_KEY
fcm.host=fcm-xmpp.googleapis.com
fcm.port=5235
XmppConfig.java
import com.alessoft.utils.Utils;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jxmpp.stringprep.XmppStringprepException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import javax.net.ssl.SSLSocketFactory;
#Configuration
class XmppConfig implements ConnectionListener {
#Value("${fcm.sender_id}")
private String senderId;
#Value("${fcm.server_key}")
private String server_key;
#Value("${fcm.host}")
private String host;
#Value("${fcm.port}")
private int port;
#Autowired
private XmppService xmppService;
private Logger logger = LoggerFactory.getLogger(XmppConfig.class);
private volatile boolean xmppConnected;
private XMPPTCPConnection xmppConnection;
public XMPPTCPConnection getXmppConnection() {
return xmppConnection;
}
#PostConstruct
public void postConstruct() throws Exception {
new Thread(() -> prepareXmppConnection()).start();
}
public XMPPConnection prepareXmppConnection() {
XMPPTCPConnectionConfiguration conf = null;
try {
conf = XMPPTCPConnectionConfiguration.builder()
.setHost(host)
.setPort(port)
.setSendPresence(false)
.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
.setSocketFactory(SSLSocketFactory.getDefault())
.setUsernameAndPassword(senderId, server_key)
.setXmppDomain("somedomain.com")
.build();
} catch (XmppStringprepException e) {
logger.info("prepareXmppConnection error", e);
}
xmppConnection = new XMPPTCPConnection(conf);
xmppConnection.addAsyncStanzaListener(xmppService, xmppService);
xmppConnection.addConnectionListener(this);
Roster.getInstanceFor(xmppConnection).setRosterLoadedAtLogin(false);
establishXmppConnection();
return xmppConnection;
}
private void establishXmppConnection() {
try {
xmppConnection.connect();
xmppConnection.login();
} catch (Exception e) {
logger.info("XMPP establishXmppConnection error", e);
}
}
#Override
public void connectionClosedOnError(Exception e) {
logger.info("LOST CONNECTION TO FCM XMPP ON ERROR", e);
Utils.sleep(5000);
establishXmppConnection();
}
#Override
public void connectionClosed() {
logger.info("LOST CONNECTION TO FCM XMPP");
Utils.sleep(5000);
establishXmppConnection();
}
#Override
public void connected(XMPPConnection connection) {
logger.info("CONNECTED TO FCM XMPP");
}
#Override
public void authenticated(XMPPConnection connection, boolean resumed) {
logger.info("AUTHENTICATED TO FCM XMPP");
}
}
XmppService.java
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.json.JSONObject;
import org.json.XML;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class XmppService implements StanzaListener, StanzaFilter {
private Logger logger = LoggerFactory.getLogger(XmppService.class);
#Autowired
private XmppConfig xmppConfig;
#Override
public void processStanza(Stanza stanzaXml) throws SmackException.NotConnectedException, InterruptedException, SmackException.NotLoggedInException {
JSONObject stanza = XML.toJSONObject(stanzaXml.toXML(null).toString());
if (!stanza.has("message")) return;
JSONObject message = stanza.getJSONObject("message");
if (message.has("type")) {
normalMessage(message);
} else {
otherMessage(message);
}
}
private void otherMessage(JSONObject message) {
JSONObject gcm = message.getJSONObject("gcm");
String contentString = gcm.getString("content");
JSONObject content = new JSONObject(contentString);
if (content.getString("message_type").equals("ack")) {
logger.info("RECEIVED ACK");
} else if (content.getString("message_type").equals("nack")) {
logger.info("RECEIVED NACK: \n" + content.toString(2));
} else {
logger.info("RECEIVED UNKNOWN: \n" + content.toString());
}
}
private void normalMessage(JSONObject message) {
JSONObject gcm = message.getJSONObject("gcm");
String contentString = gcm.getString("content");
JSONObject content = new JSONObject(contentString);
String message_id = content.getString("message_id");
String from = content.getString("from");
if (content.has("message_type")) {
logger.info("NOT FROM DEVICE:\n" + message.toString());
} else {
processMessage(content);
sendAck(message_id, from);
}
}
private void processMessage(JSONObject content) {
// your own processing
}
private void sendAck(String message_id, String regId) {
try {
JSONObject json = new JSONObject();
json.put("to", regId);
json.put("message_id", message_id);
json.put("message_type", "ack");
String messageString = String.format("<message><gcm xmlns=\"google:mobile:data\">%s</gcm></message>", json.toString());
Stanza stanza = PacketParserUtils.parseStanza(messageString);
xmppConfig.getXmppConnection().sendStanza(stanza);
} catch (Exception e) {
logger.info("fcm sending ack error", e);
}
}
private Stanza generateStanza(String messageId, JSONObject json) {
String messageString = String.format("<message id=\"%s\"><gcm xmlns=\"google:mobile:data\">%s</gcm></message>", messageId, json.toString());
try {
return PacketParserUtils.parseStanza(messageString);
} catch (Exception e) {
logger.info("generateStanza error", e);
return null;
}
}
#Override
public boolean accept(Stanza stanza) {
return true;
}
}
Related
When I'm running a Java WebSocketStompClient, I got below error:
org.eclipse.jetty.websocket.api.MessageTooLargeException: Text message size [73728] exceeds maximum size [65536]
Sample code:
import org.apache.log4j.Logger;
import org.springframework.messaging.simp.stomp.StompFrameHandler;
import org.springframework.messaging.simp.stomp.StompHeaders;
import org.springframework.messaging.simp.stomp.StompSession;
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.socket.WebSocketHttpHeaders;
import org.springframework.web.socket.client.WebSocketClient;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;
import org.springframework.web.socket.sockjs.client.SockJsClient;
import org.springframework.web.socket.sockjs.client.Transport;
import org.springframework.web.socket.sockjs.client.WebSocketTransport;
import org.springframework.web.socket.sockjs.frame.Jackson2SockJsMessageCodec;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class HelloClient {
private static Logger logger = Logger.getLogger(HelloClient.class);
StompSession session;
private final static WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
public ListenableFuture<StompSession> connect() {
Transport webSocketTransport = new WebSocketTransport(new StandardWebSocketClient());
List<Transport> transports = Collections.singletonList(webSocketTransport);
SockJsClient sockJsClient = new SockJsClient(transports);
sockJsClient.setMessageCodec(new Jackson2SockJsMessageCodec());
WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);
long[] hb = stompClient.getDefaultHeartbeat();
boolean en = stompClient.isDefaultHeartbeatEnabled();
long timeout = stompClient.getReceiptTimeLimit();
String url = "https://www.test.com";
return stompClient.connect(url, headers, new MyHandler());
}
public void subscribeMsg(StompSession stompSession) throws ExecutionException, InterruptedException {
stompSession.subscribe("/topic/test", new StompFrameHandler() {
public Type getPayloadType(StompHeaders stompHeaders) {
return byte[].class;
}
public void handleFrame(StompHeaders stompHeaders, Object o) {
logger.info("Received message " + new String((byte[]) o));
String response = new String((byte[]) o);
}
});
}
private class MyHandler extends StompSessionHandlerAdapter {
public void afterConnected(StompSession stompSession, StompHeaders stompHeaders) {
logger.info("Now connected");
session = stompSession;
}
}
public boolean isConnected() {
try {
Thread.sleep(500);
return session != null && session.isConnected();
} catch (Exception e) {
logger.warn("Error happens when checking connection status, ", e);
return false;
}
}
public static void main(String[] args) throws Exception {
HelloClient helloClient = new HelloClient();
ListenableFuture<StompSession> f = helloClient.connect();
StompSession stompSession = f.get();
helloClient.subscribeMsg(stompSession);
while (true) {
if (!helloClient.isConnected()) {
logger.info("wss diconnected ");
logger.info("need re-create ");
}
}
}
}
How to increase the limitation for a Java stomp websocket client? I found some not related answers How can I set max buffer size for web socket client(Jetty) in Java which are not suitable for stomp websocket client.
Also tried stompClient.setInboundMessageSizeLimit(Integer.MAX_VALUE); which doesn't work.
I'm trying to write an Http Server using Apache Mina.
According to Mina's architecture, there should be 2 filters for this task, one for Http Request Passing and another for processing the request and generating the response. So using the Mina example codes, I came up with the following code, that has an acceptor, logging filter, Http filter, and a filter for processing request.
Initiation of the server runs correctly, but the request does not come to DummyHttpSever filter. I tried to debug, but could not find the issue. What is going wrong here?
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.api.AbstractIoFilter;
import org.apache.mina.api.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filterchain.ReadFilterChainController;
import org.apache.mina.http.DateUtil;
import org.apache.mina.http.HttpDecoderState;
import org.apache.mina.http.HttpServerDecoder;
import org.apache.mina.http.HttpServerEncoder;
import org.apache.mina.http.api.DefaultHttpResponse;
import org.apache.mina.http.api.HttpContentChunk;
import org.apache.mina.http.api.HttpEndOfContent;
import org.apache.mina.http.api.HttpMethod;
import org.apache.mina.http.api.HttpPdu;
import org.apache.mina.http.api.HttpRequest;
import org.apache.mina.http.api.HttpStatus;
import org.apache.mina.http.api.HttpVersion;
import org.apache.mina.transport.nio.NioTcpServer;
public class HttpTest {
public static void main(String[] args) throws Exception {
NioTcpServer httpServer = new NioTcpServer();
httpServer.setReuseAddress(true);
httpServer.setFilters(new ProtocolCodecFilter<HttpPdu, ByteBuffer, Void, HttpDecoderState>(new HttpServerEncoder(),
new HttpServerDecoder()), new LoggingFilter("DECODED"), new DummyHttpSever());
httpServer.getSessionConfig().setTcpNoDelay(true);
httpServer.bind(new InetSocketAddress(8080));
// run for 20 seconds
Thread.sleep(2000000000);
httpServer.unbind();
}
private static class DummyHttpSever extends AbstractIoFilter {
private HttpRequest incomingRequest;
private List<ByteBuffer> body;
#Override
public void messageReceived(IoSession session, Object message, ReadFilterChainController controller) {
if (message instanceof HttpRequest) {
System.out.println("This shit is working");
incomingRequest = (HttpRequest) message;
body = new ArrayList<ByteBuffer>();
// check if this request is going to be followed by and HTTP body or not
if (incomingRequest.getMethod() != HttpMethod.POST && incomingRequest.getMethod() != HttpMethod.PUT) {
sendResponse(session, incomingRequest);
} else {
}
} else if (message instanceof ByteBuffer) {
body.add((ByteBuffer) message);
} else if (message instanceof HttpEndOfContent) {
// we received all the post content, send the crap back
sendResponse(session, incomingRequest);
}
}
public void sendResponse(IoSession session, HttpRequest request) {
Map<String, String> headers = new HashMap<String, String>();
headers.put("Server", "Apache MINA Dummy test server/0.0.");
headers.put("Date", DateUtil.getCurrentAsString());
headers.put("Connection", "Close");
String strContent = "Hello ! we reply to request !";
ByteBuffer content = ByteBuffer.wrap(strContent.getBytes());
// compute content len
headers.put("Content-Length", String.valueOf(content.remaining()));
session.write(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SUCCESS_OK, headers));
session.write(new HttpContentChunk(content));
session.write(new HttpEndOfContent());
session.close(false);
}
}
}
Also, following are dependencies I am using.
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-core</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-http</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-coap</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>LATEST</version>
</dependency>
This is a simple Http web server, which you can modify according to your need. This example is a modification to the example lightweight component of Apache Mina examples.
Main.java
import java.net.InetSocketAddress;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.asyncweb.common.codec.HttpCodecFactory;
import org.apache.asyncweb.examples.lightweight.HttpProtocolHandler;
import org.apache.mina.transport.socket.SocketAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
public class Main {
public static void main(String[] args) throws Exception {
SocketAcceptor acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new HttpCodecFactory()));
acceptor.setReuseAddress(true);
acceptor.getSessionConfig().setReuseAddress(true);
acceptor.getSessionConfig().setReceiveBufferSize(1024);
acceptor.getSessionConfig().setSendBufferSize(1024);
acceptor.getSessionConfig().setTcpNoDelay(true);
acceptor.getSessionConfig().setSoLinger(-1);
acceptor.setBacklog(10240);
acceptor.setHandler(new HttpProtocolHandler());
acceptor.bind(new InetSocketAddress(9012));
}
}
HttpProtocalHandler.java
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.core.future.WriteFuture;
import org.apache.asyncweb.common.HttpRequest;
import org.apache.asyncweb.common.HttpResponseStatus;
import org.apache.asyncweb.common.MutableHttpResponse;
import org.apache.asyncweb.common.DefaultHttpResponse;
import org.apache.asyncweb.common.HttpHeaderConstants;
public class HttpProtocolHandler implements IoHandler {
private static final int CONTENT_PADDING = 0; // 101
private final Map<Integer, IoBuffer> buffers = new ConcurrentHashMap<Integer, IoBuffer>();
private final Timer timer;
public HttpProtocolHandler() {
timer = new Timer(true);
}
public void exceptionCaught(IoSession session, Throwable cause)
throws Exception {
if (!(cause instanceof IOException)) {
cause.printStackTrace();
}
session.close();
}
public Dictionary extractParameters(Map hashParameters){
Dictionary parameters = new Hashtable();
Iterator it = hashParameters.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry)it.next();
parameters.put(pair.getKey(), ((ArrayList) pair.getValue()).get(0) );
// it.remove(); // avoids a ConcurrentModificationException
}
return parameters;
}
public void messageReceived(IoSession session, Object message)
throws Exception {
HttpRequest req = (HttpRequest) message;
String path = req.getRequestUri().getPath(); //path: /echo
String end_point = path;
Dictionary parameters = this.extractParameters(req.getParameters());
String response = "";
/* switch (end_point) {
case "/io":
response= new IOHandler().handleRequest(parameters);
break;
case "/cpu":
response= new CPUHandler().handleRequest(parameters);
break;
case "/db":
response= new DBHandler().handleRequest(parameters);
break;
case "/memory":
response= new MemoryHandler().handleRequest(parameters);
break;
default:
response = "No end point found";
} */
response = "No end point found";
MutableHttpResponse res;
// if (path.startsWith("/size/")) {
// doDataResponse(session, req);
// } else if (path.startsWith("/delay/")) {
// doAsynchronousDelayedResponse(session, req);
// } else if (path.startsWith("/adelay/")) {
// doAsynchronousDelayedResponse(session, req);
// } else {
res = new DefaultHttpResponse();
IoBuffer bb = IoBuffer.allocate(1024);
bb.setAutoExpand(true);
bb.putString(response.toString(), Charset.forName("UTF-8").newEncoder());
bb.flip();
res.setContent(bb);
// res.setHeader("Pragma", "no-cache");
// res.setHeader("Cache-Control", "no-cache");
res.setStatus(HttpResponseStatus.OK);
WriteFuture future = session.write(res);
if (!HttpHeaderConstants.VALUE_KEEP_ALIVE.equalsIgnoreCase(
res.getHeader( HttpHeaderConstants.KEY_CONNECTION))) {
future.addListener(IoFutureListener.CLOSE);
}
}
private void writeResponse(IoSession session, HttpRequest req,
MutableHttpResponse res) {
res.normalize(req);
WriteFuture future = session.write(res);
if (!HttpHeaderConstants.VALUE_KEEP_ALIVE.equalsIgnoreCase(
res.getHeader( HttpHeaderConstants.KEY_CONNECTION))) {
future.addListener(IoFutureListener.CLOSE);
}
}
private void doDataResponse(IoSession session, HttpRequest req) {
String path = req.getRequestUri().getPath();
int size = Integer.parseInt(path.substring(path.lastIndexOf('/') + 1))
+ CONTENT_PADDING;
MutableHttpResponse res = new DefaultHttpResponse();
res.setStatus(HttpResponseStatus.OK);
res.setHeader("ETag", "W/\"" + size + "-1164091960000\"");
res.setHeader("Last-Modified", "Tue, 31 Nov 2006 06:52:40 GMT");
IoBuffer buf = buffers.get(size);
if (buf == null) {
buf = IoBuffer.allocate(size);
buffers.put(size, buf);
}
res.setContent(buf.duplicate());
writeResponse(session, req, res);
}
private void doAsynchronousDelayedResponse(final IoSession session,
final HttpRequest req) {
String path = req.getRequestUri().getPath();
int delay = Integer.parseInt(path.substring(path.lastIndexOf('/') + 1));
final MutableHttpResponse res = new DefaultHttpResponse();
res.setStatus(HttpResponseStatus.OK);
res.setHeader("ETag", "W/\"0-1164091960000\"");
res.setHeader("Last-Modified", "Tue, 31 Nov 2006 06:52:40 GMT");
timer.schedule(new TimerTask() {
#Override
public void run() {
writeResponse(session, req, res);
}
}, delay);
}
public void messageSent(IoSession session, Object message) throws Exception {
}
public void sessionClosed(IoSession session) throws Exception {
}
public void sessionCreated(IoSession session) throws Exception {
}
public void sessionIdle(IoSession session, IdleStatus status)
throws Exception {
session.close();
}
public void sessionOpened(IoSession session) throws Exception {
session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30);
}
}
I am trying to use HTTP/2 to send an apple notification to my device on production.
I am passing this -Xbootclasspath/p:/home/mohamed/Desktop/alpn-boot-8.1.9.v20160720.jar as a Default VM arguments in eclipse.
Here are the code which i am using now:
public static void pushoNotification() {
try {
// create a low-level Jetty HTTP/2 client
HTTP2Client lowLevelClient = new HTTP2Client();
lowLevelClient.start();
// APNs requires the use of HPACK (header compression for HTTP/2), which prevents repeated header keys and values.
KeyStore ks = KeyStore.getInstance("PKCS12");
// Ensure that the password is the same as the one used later in setKeyStorePassword()
ks.load(PushNotifications.class.getClassLoader().getResourceAsStream("Prod2.p12"), "a12B34".toCharArray());
SslContextFactory ssl = new SslContextFactory(true);
ssl.setKeyStore(ks);
ssl.setKeyStorePassword("a12B34");
// create a high-level Jetty client
HttpClient client = new HttpClient(new HttpClientTransportOverHTTP2(lowLevelClient), ssl);
client.start();
// request-response exchange
ContentResponse response = client.POST("https://api.push.apple.com").path("/3/device/19297dba97212ac6fd16b9cd50f2d86629aed0e49576b2b52ed05086087da802")
.content(new StringContentProvider("{ \"aps\" : { \"alert\" : \"Hello\" } }")).send();
response.toString();
client.stop();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
My dependencies
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-http-client-transport</artifactId>
<version>9.4.0.M1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
<version>9.4.0.M1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-client</artifactId>
<version>9.4.0.M1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-client</artifactId>
<version>9.4.0.M1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-client</artifactId>
<version>9.4.0.M1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty.alpn</groupId>
<artifactId>alpn-boot</artifactId>
<version>8.1.9.v20160720</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.4.1</version>
</dependency>
but when i run this method from main method, it throws java.nio.channels.ClosedChannelException
Also i got this in eclipse console
INFO org.eclipse.jetty.http2.HTTP2Session - Failure while notifying listener org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2$SessionListenerPromise#87eaf9c
Any idea?
Thanks
I am pasting my answer now after about 3 years and 6 month just to help any one get same error, actually i can't recall how i fixed this issue but here are a working code which i am using now...
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.inject.Singleton;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.myproject.path.model.APNSObject;
public void sendIOSNotification(APNSObject apnsObject, List<String> deviceIds, String notificationType) {
HTTP2Client lowLevelClient = null;
HttpClient client = null;
String hostname = null;
InputStream inputStream = null;
try {
lowLevelClient = new HTTP2Client(); // create a low-level Jetty HTTP/2 client
lowLevelClient.start();
// APNs requires the use of HPACK (header compression for HTTP/2), which prevents repeated header keys and values.
KeyStore ks = KeyStore.getInstance(KEYSTORE_TYPE);
inputStream = getClass().getClassLoader().getResourceAsStream(APPLE_CERTIFICATE_PATH);
// Ensure that the password is the same as the one used later in setKeyStorePassword()
ks.load(inputStream, APPLE_CERTIFICATE_PASSWORD.toCharArray());
inputStream.close();
SslContextFactory ssl = new SslContextFactory(true);
ssl.setKeyStore(ks);
ssl.setKeyStorePassword(APPLE_CERTIFICATE_PASSWORD);
FuturePromise<Session> sessionPromise = new FuturePromise<>();
if (notificationType != null && notificationType.trim().equalsIgnoreCase("Development"))
hostname = APPLE_NOTIFICATION_DEVELOPMENT_HOSTNAME;
else
hostname = APPLE_NOTIFICATION_PRODUCTION_HOSTNAME;
lowLevelClient.connect(ssl, new InetSocketAddress(hostname, Integer.parseInt(APPLE_NOTIFICATION_PORT)), new ServerSessionListener.Adapter(), sessionPromise);
// create a high-level Jetty client
client = new HttpClient(new HttpClientTransportOverHTTP2(lowLevelClient), ssl);
client.start();
// logger.info("Start Sending Notification.");
for (String token : deviceIds) {
ObjectMapper mapper = new ObjectMapper();
String fcmJsonObject = mapper.setSerializationInclusion(Include.NON_NULL).setSerializationInclusion(Include.NON_EMPTY).writeValueAsString(apnsObject);
Request request = client.POST("https://" + hostname).timeout(20, TimeUnit.SECONDS).path("/3/device/" + token)
.content(new StringContentProvider(fcmJsonObject, Charset.forName("UTF-8")));
request = request.header("apns-topic", APPLE_APNS_TOPIC);
ContentResponse response = request.send();
// For development mode if it's not passed in parameters
if (response.getStatus() == 400 && !hostname.equals(APPLE_NOTIFICATION_DEVELOPMENT_HOSTNAME)) {
request = client.POST("https://" + APPLE_NOTIFICATION_DEVELOPMENT_HOSTNAME).timeout(20, TimeUnit.SECONDS).path("/3/device/" + token)
.content(new StringContentProvider(fcmJsonObject, Charset.forName("UTF-8")));
request = request.header("apns-topic", APPLE_APNS_TOPIC);
response = request.send();
logger.info("Sending notification to '" + token + "' in debugging mode...");
}
if (response.getStatus() == 200)
logger.info("Date: " + new Date() + " Notification sent successfully to '" + token + "' With Response Status: " + response.getStatus());
else
logger.warning("Sending Notification to token '" + token + "' Failed :( >> Response Status: " + response.getStatus() + "\nReason: " + response.getReason());
}
logger.info("End Sending Notification.");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (lowLevelClient != null && !lowLevelClient.isStopped())
lowLevelClient.stop();
if (client != null && !client.isStopped())
client.stop();
} catch (Exception e) {
e.printStackTrace();
}
try {
if (inputStream != null)
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
APNSObject.java class
import java.io.Serializable;
public class APNSObject implements Serializable {
// {\"aps\":{\"alert\":{" + messageTitle + "\"body\":\"" + alert + "\"}, \"sound\":\"default\"}" + payload + "}
private static final long serialVersionUID = 1L;
private Aps aps;
private TripBruEntity payload;// Our custom JSON Object
public APNSObject() {
}
public APNSObject(String title, String body, TripBruEntity payload) {
this(title, body, null, payload);
}
public APNSObject(String title, String body, String sound, TripBruEntity payload) {
aps = new Aps(title, body, sound);
this.payload = payload;
}
public Aps getAps() {
return aps;
}
public void setAps(Aps aps) {
this.aps = aps;
}
public TripBruEntity getPayload() {
return payload;
}
public void setPayload(TripBruEntity payload) {
this.payload = payload;
}
static class Aps {
private Alert alert;
private String sound;
public Aps() {
}
public Aps(String title, String body, String sound) {
this.alert = new Alert(title, body);
this.sound = sound == null || sound.isEmpty() ? "default" : sound;
}
public Alert getAlert() {
return alert;
}
public void setAlert(Alert alert) {
this.alert = alert;
}
public String getSound() {
return sound;
}
public void setSound(String sound) {
this.sound = sound;
}
static class Alert {
private String title;
private String body;
public Alert() {
}
public Alert(String title, String body) {
this.title = title;
this.body = body;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
}
}
and here are my configuration file:
APPLE_CERTIFICATE_PATH=/my/apply/certificate/path/so/plz/change/it/Certificates.p12
APPLE_CERTIFICATE_PASSWORD=myPassword(plz change it)
KEYSTORE_TYPE=PKCS12
APPLE_NOTIFICATION_DEVELOPMENT_HOSTNAME=api.development.push.apple.com
APPLE_NOTIFICATION_PRODUCTION_HOSTNAME=api.push.apple.com
APPLE_NOTIFICATION_PORT=2197
APPLE_APNS_TOPIC=MyAPNS_Topic(plz change it)
Last thing, here are jars i used in my project:
I am trying JMS 2.0 so I can decide if it is worth applying in my project. I could successfully create a send/receive application.
Now I would like to have listeners that will receive the message as soon as it is available on the queue (my final goal is to have different listeners to the same queue, each with a different message selector.
Currently I have this class:
package learning.jms;
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.jms.JMSConnectionFactory;
import javax.jms.JMSConsumer;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.JMSRuntimeException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Queue;
#Named(value="senderBean")
#SessionScoped
public class SenderBean implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Resource(mappedName="queues/myQueue")
private transient Queue myQueue;
#Inject
#JMSConnectionFactory("java:/DefaultJMSConnectionFactory")
private transient JMSContext context;
private String messageText;
private int nextType = 3;
private transient JMSConsumer consumer;
private transient JMSConsumer consumer2;
private transient JMSConsumer consumer3;
public SenderBean() {
}
#PostConstruct
public void setUp(){
}
public String getMessageText() {
return messageText;
}
public void setMessageText(String messageText) {
this.messageText = messageText;
}
public void sendJMSMessageToMyQueue() {
try {
consumer = context.createConsumer(myQueue, "type=1");
consumer.setMessageListener(new ListenerTypeOne());
// consumer2 = context.createConsumer(myQueue, "type=2");
// consumer2.setMessageListener(new ListenerTypeTwo());
//
// consumer3 = context.createConsumer(myQueue, "type=3");
// consumer3.setMessageListener(new ListenerTypeThree());
String text = "Message from producer: " + messageText;
Message m1 = context.createTextMessage(text);
m1.setIntProperty("type", nextType);
System.out.println("producer sending msg type " + nextType + "value: " + text);
nextType = (nextType++%3)+1;
context.createProducer().send(myQueue, m1);
FacesMessage facesMessage =
new FacesMessage("Sent message: " + text);
FacesContext.getCurrentInstance().addMessage(null, facesMessage);
} catch (JMSRuntimeException | JMSException t) {
System.out.println(t.toString());
}
}
private class ListenerTypeOne implements MessageListener{
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeOne:" + msg.getBody(String.class));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private class ListenerTypeTwo implements MessageListener{
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeTwo:" + msg.getBody(String.class));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private class ListenerTypeThree implements MessageListener{
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeThree:" + msg.getBody(String.class));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
I commented out two consumers, so I could focus on making one work.
I keep getting the following exception on the setMessageListener line:
javax.jms.IllegalStateException: This method is not applicable inside the application server. See the J2EE spec, e.g. J2EE1.4 Section 6.6
at org.hornetq.ra.HornetQRASession.checkStrict(HornetQRASession.java:1647)
at org.hornetq.ra.HornetQRAMessageConsumer.setMessageListener(HornetQRAMessageConsumer.java:124)
at org.hornetq.jms.client.HornetQJMSConsumer.setMessageListener(HornetQJMSConsumer.java:68)
I have no idea what could be causing this and my searches are not giving me any extra information.
I guess it could be something related to the fact that one componen should have no more than one active session. In this case, how could I create multiple listeners to listen to the queue?
(if important: I am using Wildfly 8)
EDIT
I've extracted the listener creation to a separate bean and still teh same error:
package learning.jms;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.jms.JMSConnectionFactory;
import javax.jms.JMSConsumer;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Queue;
#ApplicationScoped
public class ListenerOne {
#Inject
#JMSConnectionFactory("java:/DefaultJMSConnectionFactory")
private JMSContext context;
#Resource(mappedName="queues/myQueue")
private Queue myQueue;
private JMSConsumer consumer;
public void setUp() {
consumer = context.createConsumer(myQueue, "type=1");
consumer.setMessageListener(new ListenerTypeOne());
System.out.println("working");
}
private class ListenerTypeOne implements MessageListener{
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeOne:" + msg.getBody(String.class));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
So, looking for MDBs solved the issue.
I cleaned the senderBean classfrom any traces of the consumers I was trying to create:
package learning.jms;
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.jms.JMSConnectionFactory;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.JMSRuntimeException;
import javax.jms.Message;
import javax.jms.Queue;
#Named(value="senderBean")
#SessionScoped
public class SenderBean implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Resource(mappedName="queues/myQueue")
private transient Queue myQueue;
#Inject
#JMSConnectionFactory("java:/DefaultJMSConnectionFactory")
private transient JMSContext context;
private String messageText;
private int nextType;
public SenderBean() {
// TODO Auto-generated constructor stub
}
#PostConstruct
public void init(){
nextType=2;
}
public String getMessageText() {
return messageText;
}
public void setMessageText(String messageText) {
this.messageText = messageText;
}
public void sendJMSMessageToMyQueue() {
try {
String text = "Message from producer: " + messageText;
Message m1 = context.createTextMessage(text);
m1.setIntProperty("type", nextType);
nextType = (nextType++%3)+1;
context.createProducer().send(myQueue, m1);
FacesMessage facesMessage =
new FacesMessage("Sent message: " + text);
FacesContext.getCurrentInstance().addMessage(null, facesMessage);
} catch (JMSRuntimeException | JMSException t) {
System.out.println(t.toString());
}
}
}
(notice it is just session scope so I can iterate over the message types")
And created 3 MDBs
package learning.jms;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
#MessageDriven(activationConfig = {
#ActivationConfigProperty(propertyName = "destinationLookup",
propertyValue = "queues/myQueue"),
#ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "messageSelector",propertyValue = "type=1")
})
public class ListenerOne implements MessageListener {
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeOne: " + msg.getBody(String.class) + " type: " + msg.getIntProperty("type"));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
two:
package learning.jms;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
#MessageDriven(activationConfig = {
#ActivationConfigProperty(propertyName = "destinationLookup",
propertyValue = "queues/myQueue"),
#ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "messageSelector",propertyValue = "type=2")
})
public class ListenerTwo implements MessageListener {
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeTwo: " + msg.getBody(String.class) + " type: " + msg.getIntProperty("type"));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
three:
package learning.jms;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
#MessageDriven(activationConfig = {
#ActivationConfigProperty(propertyName = "destinationLookup",
propertyValue = "queues/myQueue"),
#ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "messageSelector",propertyValue = "type=3")
})
public class ListenerThree implements MessageListener {
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeThree: " + msg.getBody(String.class) + " type: " + msg.getIntProperty("type"));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Now they listen automatically for messages in the queue that match their selector.
Thanks #prabugp for the help :)
The above error could be because you are trying to use the client inside the same container and getting connection factory from JCA based connection factory.
Case 1: if client are remote to jms server then using jms/RemoteConnectionFactory is recommended and above problem will not be reproduced.
Case 2: if client resides in same container then connections from JCA based connection factory java/JmsXA is preferred. Since there is a limitation in JEE 7 specification under section 6:7 that JEE server does not permit EJB/Web application to have more than one active session i.e you cannot have legacy JMS application.
E.g: In a method:
public void startConnection() {
try {
TopicConnectionFactory connectionFactory = (TopicConnectionFactory) getConnectionFactory();
topicConnection = connectionFactory.createTopicConnection();
topicSession = topicConnection.createTopicSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
subscriber = topicSession.createSubscriber(messageTopic, selector, true);
MessageListener messageListener = new MessageListener(this);
// Code to set message listener.
subscriber.setMessageListener(messageListener);
topicConnection.start();
} catch (Exception e) {
LOG.error(e, e);
closeConnection();
throw new RuntimeException(e);
}
}
if connection factory on above code is from
#Resource(mappedName = "java:jboss/exported/jms/RemoteConnectionFactory")
then above code will work. But if we change the connection factory to
//#Resource(mappedName = "java:/JmsXA")
then above error will be thrown.
So if your client is in the same container then MDB should be used. Since container wants control of connection objects to support two phase commit protocol.
My view on this, you can not have a message listener in a JSF bean, since the bean lifecycle is controlled by the web container.
MDBs are the only components that are driven by messages, but JSF MBs, like EJBs or servlets, cannot listen for messages, they "live" in the context of a request, and instances are created, activated, passivated or destroyed by the container.
But instead, you can use receive(), in the context of a request, and set up some autorefresh on client side, to implement a flow that is server-side driven.
Can someone please provide me very simple example of websocket client using javax.websocket?
I want to connect to websocket (ws://socket.example.com:1234), send message (add channel) and listen to messages. All messages (sent & listened) are in JSON format.
And btw is this library the best for simple websocket communication?
I've found a great example using javax.websocket here:
http://www.programmingforliving.com/2013/08/jsr-356-java-api-for-websocket-client-api.html
Here the code based on the example linked above:
TestApp.java:
package testapp;
import java.net.URI;
import java.net.URISyntaxException;
public class TestApp {
public static void main(String[] args) {
try {
// open websocket
final WebsocketClientEndpoint clientEndPoint = new WebsocketClientEndpoint(new URI("wss://real.okcoin.cn:10440/websocket/okcoinapi"));
// add listener
clientEndPoint.addMessageHandler(new WebsocketClientEndpoint.MessageHandler() {
public void handleMessage(String message) {
System.out.println(message);
}
});
// send message to websocket
clientEndPoint.sendMessage("{'event':'addChannel','channel':'ok_btccny_ticker'}");
// wait 5 seconds for messages from websocket
Thread.sleep(5000);
} catch (InterruptedException ex) {
System.err.println("InterruptedException exception: " + ex.getMessage());
} catch (URISyntaxException ex) {
System.err.println("URISyntaxException exception: " + ex.getMessage());
}
}
}
WebsocketClientEndpoint.java:
package testapp;
import java.net.URI;
import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.ContainerProvider;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
/**
* ChatServer Client
*
* #author Jiji_Sasidharan
*/
#ClientEndpoint
public class WebsocketClientEndpoint {
Session userSession = null;
private MessageHandler messageHandler;
public WebsocketClientEndpoint(URI endpointURI) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.connectToServer(this, endpointURI);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Callback hook for Connection open events.
*
* #param userSession the userSession which is opened.
*/
#OnOpen
public void onOpen(Session userSession) {
System.out.println("opening websocket");
this.userSession = userSession;
}
/**
* Callback hook for Connection close events.
*
* #param userSession the userSession which is getting closed.
* #param reason the reason for connection close
*/
#OnClose
public void onClose(Session userSession, CloseReason reason) {
System.out.println("closing websocket");
this.userSession = null;
}
/**
* Callback hook for Message Events. This method will be invoked when a client send a message.
*
* #param message The text message
*/
#OnMessage
public void onMessage(String message) {
if (this.messageHandler != null) {
this.messageHandler.handleMessage(message);
}
}
#OnMessage
public void onMessage(ByteBuffer bytes) {
System.out.println("Handle byte buffer");
}
/**
* register message handler
*
* #param msgHandler
*/
public void addMessageHandler(MessageHandler msgHandler) {
this.messageHandler = msgHandler;
}
/**
* Send a message.
*
* #param message
*/
public void sendMessage(String message) {
this.userSession.getAsyncRemote().sendText(message);
}
/**
* Message handler.
*
* #author Jiji_Sasidharan
*/
public static interface MessageHandler {
public void handleMessage(String message);
}
}
TooTallNate has a simple client side https://github.com/TooTallNate/Java-WebSocket
Just add the java_websocket.jar in the dist folder into your project.
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_10;
import org.java_websocket.handshake.ServerHandshake;
import org.json.JSONException;
import org.json.JSONObject;
WebSocketClient mWs = new WebSocketClient( new URI( "ws://socket.example.com:1234" ), new Draft_10() )
{
#Override
public void onMessage( String message ) {
JSONObject obj = new JSONObject(message);
String channel = obj.getString("channel");
}
#Override
public void onOpen( ServerHandshake handshake ) {
System.out.println( "opened connection" );
}
#Override
public void onClose( int code, String reason, boolean remote ) {
System.out.println( "closed connection" );
}
#Override
public void onError( Exception ex ) {
ex.printStackTrace();
}
};
//open websocket
mWs.connect();
JSONObject obj = new JSONObject();
obj.put("event", "addChannel");
obj.put("channel", "ok_btccny_ticker");
String message = obj.toString();
//send message
mWs.send(message);
// and to close websocket
mWs.close();
Have a look at this Java EE 7 examples from Arun Gupta.
I forked it on github.
Main
/**
* #author Arun Gupta
*/
public class Client {
final static CountDownLatch messageLatch = new CountDownLatch(1);
public static void main(String[] args) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
String uri = "ws://echo.websocket.org:80/";
System.out.println("Connecting to " + uri);
container.connectToServer(MyClientEndpoint.class, URI.create(uri));
messageLatch.await(100, TimeUnit.SECONDS);
} catch (DeploymentException | InterruptedException | IOException ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
ClientEndpoint
/**
* #author Arun Gupta
*/
#ClientEndpoint
public class MyClientEndpoint {
#OnOpen
public void onOpen(Session session) {
System.out.println("Connected to endpoint: " + session.getBasicRemote());
try {
String name = "Duke";
System.out.println("Sending message to endpoint: " + name);
session.getBasicRemote().sendText(name);
} catch (IOException ex) {
Logger.getLogger(MyClientEndpoint.class.getName()).log(Level.SEVERE, null, ex);
}
}
#OnMessage
public void processMessage(String message) {
System.out.println("Received message in client: " + message);
Client.messageLatch.countDown();
}
#OnError
public void processError(Throwable t) {
t.printStackTrace();
}
}
Use this library org.java_websocket
First thing you should import that library in build.gradle
repositories {
mavenCentral()
}
then add the implementation in dependency{}
implementation "org.java-websocket:Java-WebSocket:1.3.0"
Then you can use this code
In your activity declare object for Websocketclient like
private WebSocketClient mWebSocketClient;
then add this method for callback
private void ConnectToWebSocket() {
URI uri;
try {
uri = new URI("ws://your web socket url");
} catch (URISyntaxException e) {
e.printStackTrace();
return;
}
mWebSocketClient = new WebSocketClient(uri) {
#Override
public void onOpen(ServerHandshake serverHandshake) {
Log.i("Websocket", "Opened");
mWebSocketClient.send("Hello from " + Build.MANUFACTURER + " " + Build.MODEL);
}
#Override
public void onMessage(String s) {
final String message = s;
runOnUiThread(new Runnable() {
#Override
public void run() {
TextView textView = (TextView)findViewById(R.id.edittext_chatbox);
textView.setText(textView.getText() + "\n" + message);
}
});
}
#Override
public void onClose(int i, String s, boolean b) {
Log.i("Websocket", "Closed " + s);
}
#Override
public void onError(Exception e) {
Log.i("Websocket", "Error " + e.getMessage());
}
};
mWebSocketClient.connect();
}
I have Spring 4.2 in my project and many SockJS Stomp implementations usually work well with Spring Boot implementations. This implementation from Baeldung worked(for me without changing from Spring 4.2 to 5). After Using the dependencies mentioned in his blog, it still gave me ClassNotFoundError. I added the below dependency to fix it.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.3.RELEASE</version>
</dependency>
Here is such a solution from - com.neovisionary.ws.client.Web socket - https://github.com/TakahikoKawasaki/nv-websocket-client
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketAdapter;
import com.neovisionaries.ws.client.WebSocketException;
import com.neovisionaries.ws.client.WebSocketFactory;
import com.neovisionaries.ws.client.WebSocketFrame;
public class WssM {
public static List<String> ListMessage = new ArrayList<>();
public static boolean WebSocketLog = true;
public static final String WssURL = "wss://site.com/api/sport_tree_ws/v1";
public static final String WebsocketMessage = "{\"type\": \"subscribe_state\", \"subscribe_state\": {\"uid\": \"-1\"}}";
public static void main(String[] args) throws IOException, WebSocketException {
WebSocket socket = connect(WssURL);
BufferedReader in = getInput();
socket.sendText(WebsocketMessage);
String text;
try {
while ((text = in.readLine()) != null) {
if (text.equals("exit")) break;
if (!socket.isOpen()) {
System.out.println("Socket is closed. Trying to reconnect...");
socket.recreate().connect();
System.out.println("Reconnected!");
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (socket.isOpen()) {
System.out.println("Disconnecting connection to server!");
socket.disconnect(); //Close the WebSocket.
}
}
}
private static WebSocket connect(String Host) throws IOException, WebSocketException {
WebSocketFactory wsFactory = new WebSocketFactory().setConnectionTimeout(55000);
WebSocket socket = wsFactory.createSocket(URI.create(Host)).addProtocol("json");//.addHeader("Sec-WebSocket-Protocol", "json")
//WebSocket socket = wsFactory.createSocket(URI.create(HOST + "?Authorization=" + DUMMY_JWT_TOKEN));
socket.addListener(new WebSocketAdapter() {
#Override
public void onSendingHandshake(WebSocket websocket, String requestLine, List<String[]> headers) {
if (WebSocketLog) System.out.println(requestLine);
for (String[] header : headers) { //Print the header, "{name}: {value}"
if (WebSocketLog) System.out.format("%s: %s\n", header[0], header[1]);
}
}
#Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) {
if (WebSocketLog) System.out.println("Success! WebSocket - Connected!");
}
#Override
public void onTextMessage(WebSocket websocket, String text) {
if (WebSocketLog) System.out.printf("MessageToClient: %s%n", text); ListMessage.add(text);
}
#Override
public void onDisconnected(WebSocket websocket, WebSocketFrame serverCloseFrame, WebSocketFrame clientCloseFrame, boolean closedByServer) {
if (WebSocketLog) System.out.println("Disconnecting...");
if (WebSocketLog) System.out.printf(" Opcode: %d%n", serverCloseFrame.getOpcode());
}
#Override
public void onPongFrame(WebSocket websocket, WebSocketFrame frame) {
if (WebSocketLog) System.out.printf("Received some pong..!! Payload text: %s%n", frame.getPayloadText());
System.out.printf(" Opcode: %d%n", frame.getOpcode());
}
#Override
public void onPingFrame(WebSocket websocket, WebSocketFrame frame) {
if (WebSocketLog) System.out.printf("I have been pinged by server at %s%n", LocalDateTime.now());
websocket.sendPong("Ponging from client");
}
#Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) {
if (WebSocketLog) System.out.printf("onTextFrame - %s%n", LocalDateTime.now());
websocket.sendPong("onTextFrame from client");
}
#Override
public void onError(WebSocket websocket, WebSocketException cause) {
System.out.printf("I have received exception %s%n", cause.getMessage());
}
}).connect();
return socket;
}
private static BufferedReader getInput() {
return new BufferedReader(new InputStreamReader(System.in));
}
}