I followed the examples that I could find for making a Cometd java client application. I am trying to make it so when the user presses a button on the screen, cometd publishes a message to the server. Right now the publish gets called but the server never receives message to its listener. I have this server listener working with javascript but not java code.
Client side setup is as follows:
// Prepare the transport
Map<String, Object> options = new HashMap<String, Object>();
ClientTransport transport = LongPollingTransport.create(options, httpClient);
_client = new BayeuxClient("http://10.100.97.168:8888/slideshow/slideshow/", transport);
_client.getChannel(Channel.META_HANDSHAKE).addListener(new InitializerListener());
_client.getChannel(Channel.META_CONNECT).addListener(new ConnectionListener());
My handshake and connection listeners:
private class InitializerListener implements ClientSessionChannel.MessageListener
{
public void onMessage(ClientSessionChannel channel, Message message)
{
if (message.isSuccessful())
{
_handshaked = true;
}
else
{
_handshaked = false;
}
}
}
private class ConnectionListener implements ClientSessionChannel.MessageListener
{
private boolean wasConnected;
private boolean connected;
public void onMessage(ClientSessionChannel channel, Message message)
{
if (_client.isDisconnected())
{
connected = false;
connectionClosed();
return;
}
wasConnected = connected;
connected = message.isSuccessful();
if (!wasConnected && connected)
{
connectionEstablished();
}
else if (wasConnected && !connected)
{
connectionBroken();
}
}
}
Button press that tries to publish:
_btnPlay.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if(_connection_established == true)
{
Map<String, Object> data = new HashMap<String, Object>();
_clientSessionChannel.publish(data);
}
}
});
Just in case here is my server Listener too:
#Listener("/service/slide/play")
public void processServiceSlidePlay(ServerSession client, ServerMessage message)
{
synchronized(imagelock)
{
if(slideShowRunning == false && imageIDList != null && imageIDList.size() > 0)
{
slideShowRunning = true;
if(imageIDList != null && imageIDList.size() > 0)
{
deliverChangeMessage(client, message);
}
}
}
}
So I can establish a connection and my publish gets called, but the server never picks it up. Also I noticed if I do the same exact publish but in my handshake or connect callback, the server will pick it up. Seems like I just can't do a publish outside of the callbacks.
Thanks in Advance for the Help.
It turned out that I was running the network, cometD code, on Androids Main UI Thread and that is not allowed. Once I moved all the cometd code over to an AsyncTask it all started working all the time.
Related
I am trying to use the following code which is an implementation of web sockets in Netty Nio. I have implment a JavaFx Gui and from the Gui I want to read the messages that are received from the Server or from other clients. The NettyClient code is like the following:
public static ChannelFuture callBack () throws Exception{
String host = "localhost";
int port = 8080;
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new RequestDataEncoder(), new ResponseDataDecoder(),
new ClientHandler(i -> {
synchronized (lock) {
connectedClients = i;
lock.notifyAll();
}
}));
}
});
ChannelFuture f = b.connect(host, port).sync();
//f.channel().closeFuture().sync();
return f;
}
finally {
//workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
ChannelFuture ret;
ClientHandler obj = new ClientHandler(i -> {
synchronized (lock) {
connectedClients = i;
lock.notifyAll();
}
});
ret = callBack();
int connected = connectedClients;
if (connected != 2) {
System.out.println("The number if the connected clients is not two before locking");
synchronized (lock) {
while (true) {
connected = connectedClients;
if (connected == 2)
break;
System.out.println("The number if the connected clients is not two");
lock.wait();
}
}
}
System.out.println("The number if the connected clients is two: " + connected );
ret.channel().read(); // can I use that from other parts of the code in order to read the incoming messages?
}
How can I use the returned channelFuture from the callBack from other parts of my code in order to read the incoming messages? Do I need to call again callBack, or how can I received the updated message of the channel? Could I possible use from my code (inside a button event) something like ret.channel().read() (so as to take the last message)?
By reading that code,the NettyClient is used to create connection(ClientHandler ),once connect done,ClientHandler.channelActive is called by Netty,if you want send data to server,you should put some code here. if this connection get message form server, ClientHandler.channelRead is called by Netty, put your code to handle message.
You also need to read doc to know how netty encoder/decoder works.
How can I use the returned channelFuture from the callBack from other parts of my code in order to read the incoming messages?
share those ClientHandler created by NettyClient(NettyClient.java line 29)
Do I need to call again callBack, or how can I received the updated message of the channel?
if server message come,ClientHandler.channelRead is called.
Could I possible use from my code (inside a button event) something like ret.channel().read() (so as to take the last message)?
yes you could,but not a netty way,to play with netty,you write callbacks(when message come,when message sent ...),wait netty call your code,that is : the driver is netty,not you.
last,do you really need such a heavy library to do network?if not ,try This code,it simple,easy to understanding
I have tried to call no of web services in a sequential manner like one by one as below. Once all web services run successfully task is over. If not then there must be showing the alert to the user.
Code:
Dialog progressDialog = ComponentUtils.getFormattedDialog(new Dialog());
progressDialog.showModeless();
boolean allDone = true;
for(int i=0;i<serviceList.size();i++){
String serviceUrl = serviceList.get(i);
boolean service = getServiceResponse(serviceUrl);
if(service==false){
progressDialog.dispose();
allDone = false;
break;
}
}
if(allDone){
progressDialog.dispose();
Dialog.show("SUCCESS","Process Done","OK",null);
}
else{
Dialog.show("FAIL","Process Failed","OK",null)
}
...
public static boolean getServiceResponse(String serviceUrl){
boolean isSuccess = false;
ConnectionRequest connectionRequest = new ConnectionRequest() {
#Override
protected void handleErrorResponseCode(int code, String message) {
this.kill();
LogUtil.setErrorLog(message,page_name+ " > handleErrorResponseCode");
isSuccess = false
}
#Override
protected void handleException(Exception err) {
this.kill();
LogUtil.setErrorLog(err,page_name + " > handleException");
isSuccess = false
}
#Override
protected void readResponse(InputStream input) {
isSuccess = true
}
};
connectionRequest.setUrl(serviceUrl);
connectionRequest.setContentType("application/x-www-form-urlencoded");
connectionRequest.setPost(true);
connectionRequest.setDuplicateSupported(true);
connectionRequest.setTimeout(100000);
NetworkManager.getInstance().addToQueueAndWait(connectionRequest);
return isSuccess;
}
Whenever I am trying to sync process in the full network it works fine as aspected.
But during the process, if network runs slow or lost then it will not tend to alert the user to the issue. instead, it just sticks on process dialog.
I have added error log in one file to check later on for the issue. But that also not showing any error in this case.
Any help will be more appreciable.
Timeout in Codename One is currently limited to connection timeout and doesn't apply to read timeout so once a connection is made it will last. You can use a progress listener on the NetworkManager to detect such situations and kill the connection.
This question is Extension of my previous question on this SO question "How to connect XMPP bosh server using java smack library?"
I am using Java as server side language. I have successfully implement xmpp BOSH connection using smach-jbosh thanks to #Deuteu for helping me to achieve this, so far I have modify jbosh's BOSHClient.java file and added two getter method for extracting RID and SID.
Now I have RID and SID on my app server (I am using Apache Tomcat). I need to pass this credential to Strophe (web client) so that it can attach to connection.
Here I have some doubt.
When to disconnect bosh Connection establish from the app server? before passing sid, rid and jid to strophe or after passing sid, rid and jid to strophe?
As per my observation during implementation for the same, I have observed that once bosh connection from the app server has been disconnected, session is expired and SID and RID is no longer useful!!!
I have implemented this logic (Establishing bosh connection and Extracting sid and rid) on a Servlet, here once response has been send from Servlet, Thread will get expired and end BOSH connection will get terminated, so I am not able perform `Attach()` on strophe as session is expired.
Can somebody help me with that problem?
I believe #fpsColton's answer is correct - I'm just added extra info for clarity. As requested on linked thread here is the code changes I made on this - note: I only added the parts where I've labelled "DH"
In BOSHConnection:
// DH: function to preserve current api
public void login(String username, String password, String resource)
throws XMPPException {
login(username, password, resource, false);
}
// DH: Most of this is existing login function, but added prebind parameter
// to allow leaving function after all required pre-bind steps done and before
// presence stanza gets sent (sent from attach in XMPP client)
public void login(String username, String password, String resource, boolean preBind)
throws XMPPException {
if (!isConnected()) {
throw new IllegalStateException("Not connected to server.");
}
if (authenticated) {
throw new IllegalStateException("Already logged in to server.");
}
// Do partial version of nameprep on the username.
username = username.toLowerCase().trim();
String response;
if (config.isSASLAuthenticationEnabled()
&& saslAuthentication.hasNonAnonymousAuthentication()) {
// Authenticate using SASL
if (password != null) {
response = saslAuthentication.authenticate(username, password, resource);
} else {
response = saslAuthentication.authenticate(username, resource, config.getCallbackHandler());
}
} else {
// Authenticate using Non-SASL
response = new NonSASLAuthentication(this).authenticate(username, password, resource);
}
// Indicate that we're now authenticated.
authenticated = true;
anonymous = false;
// DH: Prebind only requires connect and authenticate
if (preBind) {
return;
}
// Set the user.
if (response != null) {
this.user = response;
// Update the serviceName with the one returned by the server
config.setServiceName(StringUtils.parseServer(response));
} else {
this.user = username + "#" + getServiceName();
if (resource != null) {
this.user += "/" + resource;
}
}
// Create the roster if it is not a reconnection.
if (this.roster == null) {
this.roster = new Roster(this);
}
if (config.isRosterLoadedAtLogin()) {
this.roster.reload();
}
// Set presence to online.
if (config.isSendPresence()) {
sendPacket(new Presence(Presence.Type.available));
}
// Stores the autentication for future reconnection
config.setLoginInfo(username, password, resource);
// If debugging is enabled, change the the debug window title to include
// the
// name we are now logged-in as.l
if (config.isDebuggerEnabled() && debugger != null) {
debugger.userHasLogged(user);
}
}
and
// DH
#Override
public void disconnect() {
client.close();
}
then my Client-side (Web Server) wrapper class - for connecting from within JSP is:
Note: This is proving code rather than production - so there's some stuff in here you may not want.
public class SmackBoshConnector {
private String sessionID = null;
private String authID = null;
private Long requestID = 0L;
private String packetID = null;
private boolean connected = false;
public boolean connect(String userName, String password, String host, int port, final String xmppService) {
boolean success = false;
try {
Enumeration<SaslClientFactory> saslFacts = Sasl.getSaslClientFactories();
if (!saslFacts.hasMoreElements()) {
System.out.println("Sasl Provider not pre-loaded");
int added = Security.addProvider(new com.sun.security.sasl.Provider());
if (added == -1) {
System.out.println("Sasl Provider could not be loaded");
System.exit(added);
}
else {
System.out.println("Sasl Provider added");
}
}
BOSHConfiguration config = new BOSHConfiguration(false, host, port, "/http-bind/", xmppService);
BOSHConnection connection = new BOSHConnection(config);
PacketListener sndListener = new PacketListener() {
#Override
public void processPacket(Packet packet) {
SmackBoshConnector.this.packetID = packet.getPacketID();
System.out.println("Send PacketId["+packetID+"] to["+packet.toXML()+"]");
}
};
PacketListener rcvListener = new PacketListener() {
#Override
public void processPacket(Packet packet) {
SmackBoshConnector.this.packetID = packet.getPacketID();
System.out.println("Rcvd PacketId["+packetID+"] to["+packet.toXML()+"]");
}
};
PacketFilter packetFilter = new PacketFilter() {
#Override
public boolean accept(Packet packet) {
return true;
}
};
connection.addPacketSendingListener(sndListener, packetFilter);
connection.addPacketListener(rcvListener, packetFilter);
connection.connect();
// login with pre-bind only
connection.login(userName, password, "", true);
authID = connection.getConnectionID();
BOSHClient client = connection.getClient();
sessionID = client.getSid();
requestID = client.getRid();
System.out.println("Connected ["+authID+"] sid["+sessionID+"] rid["+requestID+"]");
success = true;
connected = true;
try {
Thread.yield();
Thread.sleep(500);
}
catch (InterruptedException e) {
// Ignore
}
finally {
connection.disconnect();
}
} catch (XMPPException ex) {
Logger.getLogger(SmackBoshConnector.class.getName()).log(Level.SEVERE, null, ex);
}
return success;
}
public boolean isConnected() {
return connected;
}
public String getSessionID() {
return sessionID;
}
public String getAuthID() {
return authID;
}
public String getRequestIDAsString() {
return Long.toString(requestID);
}
public String getNextRequestIDAsString() {
return Long.toString(requestID+1);
}
public static void main(String[] args) {
SmackBoshConnector bc = new SmackBoshConnector();
bc.connect("dazed", "i3ji44mj7k2qt14djct0t5o709", "192.168.2.15", 5280, "my.xmppservice.com");
}
}
I confess that I'm don't fully remember why I put the Thread.yield and Thread.sleep(1/2 sec) in here - I think - as you can see with added PacketListener - the lower level functions return after sending data and before getting a response back from the server - and if you disconnect before the server has sent it's response then it (also) causes it to clean up the session and things won't work. However it may be that, as #fpsColton says, this dicsonnect() isn't actually required.
Edit: I now remember a bit more about whay I included sleep() and yield(). I noticed that Smack library includes sleep() in several places, including XMPPConnection.shutdown() as per source. Plus in terms of yield() I had problems in my environment (Java in Oracle Database - probably untypical) when it wasn't included - as per Smack Forum Thread.
Good luck.
After you have created a BOSH session with smack and have extracted the SID+RID values, you need to pass them to Strophe's attach() and from here on out you need to let strophe deal with this connection. Once Strophe has attached, you do not want your server to be doing anything to the connection at all.
If your server side code sends any messages at all to the connection manager after strophe has attached, it's likely that it will send a invalid RID which will cause your session to terminate.
Again, once the session has been established and is usable by strophe, do not attempt to continue using it from the server side. After your server side bosh client completes authentication and you've passed the SID+RID to the page, just destroy the server side connection object, don't attempt to disconnect or anything as this will end your session.
The thing you need to remember is, unlike traditional XMPP connections over TCP, BOSH clients do NOT maintain a persistent connection to the server (this is why we use BOSH in web applications). So there is nothing to disconnect. The persistent connection is actually between the XMPP server and the BOSH connection manager, it's not something you need to deal with. So when you call disconnect from your server side BOSH client, you're telling the connection manager to end the session and close it's connection to the XMPP server, which completely defeats the purpose of creating the session in the first place.
I'm using websockets to make a multiplayer game and I need to send multiple types of data across the server but when I connect to the server it's supposed to send back a name and number ("type") and ("data") respectively from the websocket library on connection. I don't need the type but ("data") is vital for the game logic to actually work.
Below is the code I have in my websockets onMessage() function:
#Override
public void onMessage(String message)
{
try
{
JSONObject json = new JSONObject(message);
if(json.has("type") && json.has("data"))
{
Log.d(TAG, json.getString("type"));
Log.d(TAG, json.getString("data"));
playerNum = Integer.parseInt(json.getString("data"));
Log.d(TAG,"Received... Type : " +json.getString("type")+" Data : "+json.getString("data"));
}
if(json.has("Player1TurnOver"))
{
player1TurnOver = json.getBoolean("Player1TurnOver");
}
if(json.has("Word"))
{
String b = json.getString("Word");
bWord = new char[b.length()];
for(int i = 0; i < b.length(); i++)
{
bWord[i] = b.charAt(i);
}
wordLength = bWord.length;
}
}
catch(JSONException e)
{
}
}
But this is never called from the server even though the client has a listener as such:
mClient = new WebSocketClient(URI.create("ws://some_ip:8080/wstest"), new WebSocketClient.Listener()){
And the listener is initialised within the websocket library class
public interface Listener {
public void onConnect();
public void onMessage(String message);
public void onMessage(byte[] data);
public void onDisconnect(int code, String reason);
public void onError(Exception error);
}
I can't seem to figure out why this isn't working properly. As it has worked before...
Sometimes it is not correctly detected when a device looses internet connection (Java is not that smart in this case ;) )
Apart from this. Could you maybe activate the debug printouts with WebSocketImpl.DEBUG = true;
The lib will automatically send pings to the endpoints at specific interval and if no ping was received it assumes that the endpoint got disconnected!
I am wondering if there is the possibility to know when a message has been delivered. Since I want to shutdown the actorsystem right afterwards. The code below connects to a remote actor and then sends a message. But in some cases the local actorsystem seems to be shut down too early.
ActorSystem system = ActorSystem.create("Test", config.getConfig("webbackend"));
ActorSelection communicator = system.actorSelection("akka.tcp://Midas#127.0.0.1:2555/user/Communicator");
communicator.tell(new TimerTransmissionCmd(channel.getId()), ActorRef.noSender());
//system.shutdown();
Try adding this code after send the message:
Boolean wasProcessed = (Integer)Await.result(Patterns.ask(communicator, new ResultClass(), 5000),
Duration.create(5000, TimeUnit.MILLISECONDS));
if(wasProcessed){
actorSystem.shutdown();
}
You also have to add this in your Actor class:
private boolean wasProcessed = false;
#Override
public void onReceive(Object messageReceived) throws Exception {
if (messageReceived instanceof ResultClass) {
this.workerActor1.tell(wasProcessed, getSender());
} else {
//Put your process code here
wasProcessed = true;
}
}
But I recommend you configure a prudential timeout and after that always shutdown the system.