How to check if WebSocket is connected - java

I created a Websocket service class using KOUSH library https://github.com/koush/AndroidAsync#can-also-create-web-sockets
Now I want to check if the Websocket is connected to my server from my mainActivity. Do you know how I can check this?
thanks
AsyncHttpClient.getDefaultInstance().websocket(get, "my-protocol", new WebSocketConnectCallback() {
#Override
public void onCompleted(Exception ex, WebSocket webSocket) {
if (ex != null) {
ex.printStackTrace();
return;
}
webSocket.send("a string");
webSocket.send(new byte[10]);
webSocket.setStringCallback(new StringCallback() {
public void onStringAvailable(String s) {
System.out.println("I got a string: " + s);
}
});
webSocket.setDataCallback(new DataCallback() {
public void onDataAvailable(ByteBufferList byteBufferList) {
System.out.println("I got some bytes!");
// note that this data has been read
byteBufferList.recycle();
}
});
}
});

You can do something like this:
if (mWebSocketClient.isOpen) {
mWebSocketClient.send(jObj.toString())
} else {
try {
client.connectWebSocket(p0)
mWebSocketClient.send(jObj.toString())
} catch (e : Exception){
Toast.makeText(p0, "not connected $e", Toast.LENGTH_LONG).show()
}
}

The solution is to save the result of this Function to a local Websocket Variable , and then you check its status :
WebSocket webSocket=null;
try {
webSocket=AsyncHttpClient.getDefaultInstance().websocket(get, "my-protocol", new WebSocketConnectCallback() {
#Override
public void onCompleted(Exception ex, WebSocket webSocket) {
if (ex != null) {
ex.printStackTrace();
return;
}
webSocket.send("a string");
webSocket.send(new byte[10]);
webSocket.setStringCallback(new StringCallback() {
public void onStringAvailable(String s) {
System.out.println("I got a string: " + s);
}
});
webSocket.setDataCallback(new DataCallback() {
public void onDataAvailable(ByteBufferList byteBufferList) {
System.out.println("I got some bytes!");
// note that this data has been read
byteBufferList.recycle();
}
});
}
}).get();
}catch(InterruptedException e){
//handle exception
}catch(ExecutionException e){
//handle exception
}
if(websocket!=null && websocket.isOpen()){
//websocket is working fine
}
you can use other statements like websocket.isClosed();

Related

RxJava error handling in chain API calls

I have playing with Rxjava recently trying to implement a chain of events(Api callas/Database operations) and seem to have hit a roadblock when it comes to handling errors.
This is what I am trying to do. I am calling an Api that will check if user exists in the database. Based on the response I get, I am trying to chain a few sequences using rxjava. Following diagram might explain a little better.
checkUser()
/ \
No Yes
/ \
createUserRemote() FetchUserNotesRemote()
| |
End SaveUserNotesLocal()
|
End
I am able to chain together checkUser() -> FetchUserNotesRemote() -> SaveUserNotesLocal() sequence with the following code.
checkUser()
.flatMap(id -> {return fetchData(id);})
.flatMap(notesResponseObject -> {return saveFetchedData(notesResponseObject);})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Integer>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onSuccess(Integer integer) {
//handle onsuccess here
}
#Override
public void onError(Throwable e) {
//handle errors here
}
});
The issue I am mainly trying to solve.
I can't figure out how to handle a case where checkUser() returns
a 404 http status. Because when that happens, subscriber's onError
method gets called which seems to me is what should happen. How can I
handle it so that when I get an error (404) response from API,
instead of executing FetchUserNotesRemote() and SaveUserNotesLocal(),
I execute a different chain of events?
Another thing I am not sure about is, if there is an error called on
any of the observables in a chain, how does the subscriber's onError method know
which observable called it?
1) To execute different chain of observables on error you can use method onErorrResumeNext(). More info here: github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators
Example:
checkUser().flatMap(id -> {return fetchData(id);})
.flatMap(notesResponseObject -> {return saveFetchedData(notesResponseObject);})
.onErrorResumeNext(throwable -> { return doSomethingDifferent(); }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Integer>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onSuccess(Integer integer) {
//handle onsuccess here
}
#Override
public void onError(Throwable e) {
//handle errors here
}
});
2) If the exception is thrown somewhere in your stream, it is passed down to subscriber onError(). If you want to know at which part of stream error was thrown, you can add multiple onErorrResumeNext() calls, that throw concrete exception after each api call.
checkUser()
.onErrorResumeNext(throwable -> { return Observable.error(new CheckUserException()); }
.flatMap(id -> {return fetchData(id);})
.onErrorResumeNext(throwable -> { return Observable.error(new FetchDataException()); }
.flatMap(notesResponseObject -> {return saveFetchedData(notesResponseObject);})
.onErrorResumeNext(throwable -> { return Observable.error(new SaveDataException()); }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Integer>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onSuccess(Integer integer) {
//handle onsuccess here
}
#Override
public void onError(Throwable e) {
//handle errors here
}
});
I completely forgot about this. But #mol pushed me in the right direction. My solution was a bit different. This may not be the best solution but it worked for me at the time.
I first created my own custom exception classes like following.
public class CreateUserLocalException extends Exception {
public CreateUserLocalException(String message) {
super(message);
}
}
Then in my checkUser() function I throw exception of type I created above like following.
public Single<String> checkUser(String id) {
return Single.create(new SingleOnSubscribe<String>() {
#Override
public void subscribe(SingleEmitter<String> emitter) throws Exception {
try {
GetUserResponseObject getUserResponseObject = apiClient.usersIdGet(id);
Log.d("Test", "checkUserCall: Status: " + getUserResponseObject.getStatus());
emitter.onSuccess(getUserResponseObject.getBody().getUserId());
} catch (AmazonServiceException e) {
Log.d("Test", "AmazonServiceException : " + e.getErrorMessage());
e.printStackTrace();
if (e.getErrorMessage().equals("timeout")) {
throw new SocketTimeoutException();
} else {
throw new CheckUserException(Integer.toString(e.getStatusCode()));
}
} catch (Exception e) {
e.printStackTrace();
throw new CheckUserException(Integer.toString(AppConstants.ERROR));
}
}
});
}
Then in my chain of calls, in the event of an error, onError(throwable) gets invoked where I am checking the instanceof Exception to identify what kind of exception occurred. Below is the code for chain of functions.
cloudSyncHelper.checkUser(user.getUser_id())
.retry(3, new Predicate<Throwable>() {
#Override
public boolean test(Throwable throwable) throws Exception {
Log.d("Test", throwable.toString());
if (throwable instanceof SocketTimeoutException) {
Log.d("Test", "Time out.. Retrying..");
return true;
}
return false;
}
})
.flatMap(s -> {
return cloudSyncHelper.createUserLocal(user)
.onErrorResumeNext(throwable -> {
Log.d("Test", "onErrorResumeNext, throwable message: " + throwable.getMessage());
if (throwable instanceof CreateUserLocalException) {
if (Integer.parseInt(throwable.getMessage()) == AppConstants.LOCAL_DB_DUPLICATE) {
return Single.just(user.getUser_id());
}
}
return Single.error(new CreateUserLocalException(Integer.toString(AppConstants.LOCAL_DB_ERROR)));
});
})
.flatMap(id -> {
return cloudSyncHelper.fetchData(id)
.retry(3, new Predicate<Throwable>() {
#Override
public boolean test(Throwable throwable) throws Exception {
Log.d("Test", throwable.toString());
if (throwable instanceof SocketTimeoutException) {
Log.d("Test", "Time out.. Retrying..");
return true;
}
return false;
}
});
})
.flatMap(notesResponseObject -> {
return cloudSyncHelper.saveFetchedData(notesResponseObject);
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Integer>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onSuccess(Integer integer) {
//handle onsuccess here
googleSignInButton.setEnabled(true);
progressBar.setVisibility(View.GONE);
Log.d("Test", "onSuccess Called");
getSharedPreferences(AppConstants.AppName, MODE_PRIVATE).edit().putBoolean("isFirstRun", false).apply();
startActivity(new Intent(LoginScreen.this, HomeScreen.class));
}
#Override
public void onError(Throwable e) {
if (e instanceof SocketTimeoutException) {
googleSignInButton.setEnabled(true);
progressBar.setVisibility(View.GONE);
Log.d("Test", "Socket Time Out");
Utils.createToast(LoginScreen.this, "Socket timed out");
return;
}
int code = Integer.parseInt(e.getMessage());
Log.d("Test", "onError Called");
if (e instanceof CheckUserException) {
Log.d("Test", "onError CheckUserException");
if (code == AppConstants.NOTFOUND) {
newUserSequence(user);
} else {
googleSignInButton.setEnabled(true);
progressBar.setVisibility(View.GONE);
Utils.createToast(LoginScreen.this, "Unable to user information from cloud. Try again.");
}
}
if (e instanceof CreateUserLocalException) {
Log.d("Test", "onError CreateUserLocalException");
googleSignInButton.setEnabled(true);
progressBar.setVisibility(View.GONE);
}
if (e instanceof FetchDataException) {
Log.d("Test", "onError FetchDataException");
if (code == AppConstants.NOTFOUND) {
googleSignInButton.setEnabled(true);
progressBar.setVisibility(View.GONE);
getSharedPreferences(AppConstants.AppName, MODE_PRIVATE).edit().putBoolean("isFirstRun", false).apply();
startActivity(new Intent(LoginScreen.this, HomeScreen.class));
} else {
googleSignInButton.setEnabled(true);
progressBar.setVisibility(View.GONE);
Log.d("Test", "Unable to fetch data from cloud");
Utils.createToast(LoginScreen.this, "Unable to fetch data from cloud. Try again.");
}
}
if (e instanceof SaveDataLocalException) {
googleSignInButton.setEnabled(true);
progressBar.setVisibility(View.GONE);
Log.d("Test", "onError SaveDataLocalException");
if (code == AppConstants.LOCAL_DB_ERROR) {
Log.d("Test", "Unable to save data fetched from cloud");
Utils.createToast(LoginScreen.this, "Unable to save data fetched from cloud");
} else {
Utils.createToast(LoginScreen.this, "Unable to save data fetched from cloud");
}
}
}
});
Hope this helps.

Initializing a new socket in Java increases RAM usage

I use the method below to keep my connection alive if it closes. I noticed that when it had run for a week and there was hundreth socket going on, the RAM usage had increased by 700 MB. Am I doing something wrong?
If it runs for a week without needing to initialize so many new sockets, the RAM usage is smaller.
import ws.JettyWebSocketClient;
public class connectionKeeper extends Thread {
public void run(){
lib.print(">>> Connection thread running");
do{
lib.writeLog("Opening new websocket connection");
try{
GVL.socketCounter++;
GVL.ws = new JettyWebSocketClient();
GVL.ws.run();
}catch(Exception e){
e.printStackTrace();
lib.error("Error: connectionKeeper: " + e.toString());
}
// if we are here, we got an error or the socket has executed the run() -method to end
lib.sleep(2000);
}while(true);
}
}
-
public class JettyWebSocketClient {
private boolean connected=false;
private WebSocketClient client=new WebSocketClient();
public void run() {
MyWebSocket socket = new MyWebSocket();
ClientUpgradeRequest request;
URI destinationUri = null;
try {
destinationUri = new URI("wss://example.com:3000/ws");
} catch (URISyntaxException e1) {
e1.printStackTrace();
lib.error("Jetty.runA(): " + e1.toString());
}
SslContextFactory sslContextFactory = new SslContextFactory();
Resource keyStoreResource = Resource.newResource(this.getClass().getResource("/cert.jks"));
sslContextFactory.setKeyStoreResource(keyStoreResource);
sslContextFactory.setKeyStorePassword("pass");
client=new WebSocketClient(sslContextFactory);
connected=false;
try {
client.start();
request = new ClientUpgradeRequest();
System.out.println("SOCKET" + GVL.socketCounter+ ":\tConnecting to " + destinationUri.toString());
client.connect(socket, destinationUri, request);
do{
socket.awaitClose(10);
}while(connected);
} catch (Throwable t) {
t.printStackTrace();
lib.error("Jetty.runB(): " + t.toString());
}
}
public boolean send(JSONObject message){
String msg=message.toString();
System.out.println("SOCKET" + GVL.socketCounter+ ":\tSending msg:\t" + msg);
for(Session s: client.getOpenSessions()) {
if (s.isOpen()) {
try {
s.getRemote().sendString(msg);
return true;
} catch (IOException e) {
e.printStackTrace();
lib.error(e.toString());
}
}
}
return false;
}
public String status(){
return this.client.getState();
}
public boolean isConnected() {
return connected;
}
public void disconnect(){
lib.print("Disconnecting...");
setConnected(false);
try {
try{
client.stop();
} catch (InterruptedException e) {
// sometimes it gets here, sometimes not.. hmm
}
} catch(Exception a){
lib.error("Jetty.disconnect():\t" + a.toString());
}
lib.print("Disconnected...");
}
public void setConnected(boolean newval) {
connected=newval;
}
#WebSocket
public class MyWebSocket {
private final CountDownLatch closeLatch = new CountDownLatch(1);
#OnWebSocketConnect
public void onConnect(Session session) {
System.out.println("SOCKET" + GVL.socketCounter+ ":\tCONNECTED");
setConnected(true);
}
#OnWebSocketMessage
public void onMessage(String message) {
messaging.handleMsg(message); // this method uses received data to calculate some things
}
public void onError(int statusCode, String reason){
lib.error("SOCKET" + GVL.socketCounter+ ":\tError:\t" + reason + " / Code: " + statusCode);
}
#OnWebSocketClose
public void onClose(int statusCode, String reason) {
lib.error("SOCKET" + GVL.socketCounter+ ":\tClosed:\t" + reason + " / Code: " + statusCode);
setConnected(false);
}
public void awaitClose(int n) {
try {
this.closeLatch.await(n, TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
lib.error("SOCKET" + GVL.socketCounter+ ": Jetty.awaitClose():" + e.toString());
disconnect(); // useless?
}
}
}
}
Don't keep recreating the WebSocketClient object, just create 1 of those and reconnect when you want to.
Think of the WebSocketClient as the browser.
Each client.start() as you starting up that browser.
Each client.connect() as you opening a tab to a new web page.
The expensive operation, starting the browser, you are doing over and over and over again.
The cheap operation, connecting to a website, opening new tabs, closing others, you are not doing.
Instead, you are going "dang, i need to reconnect, let me stop the browser, and restart it to reconnect"

How can I store and load my Sessions as a User on Parse?

I am currently developing an Android app for existing IOS app, so using Session object to store some data on Parse is crucial for me.
As for creating and uploading sessions to Parse I have no problems.
public static void syncUser() {
ParseUser.getCurrentUser().fetchInBackground(new GetCallback<ParseObject>() {
#Override
public void done(ParseObject object, ParseException e) {
if (e != null) {
Log.d("", "SYNC ERROR");
e.printStackTrace();
}
if (object != null) {
syncSessions();
}
}
});
}
private static void syncSessions() {
ParseQuery<ParseSession> query = ParseQuery.getQuery(ParseSession.class);
query.fromLocalDatastore();
query.findInBackground(new FindCallback<ParseSession>() {
#Override
public void done(List<ParseSession> objects, ParseException e) {
for (ParseSession session : objects) {
fetchSession(session, null);
}
}
});
}
public static void fetchSession(final ParseSession session, final OnResultCallback cb) {
session.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e != null) {
if (cb != null)
cb.onResult(false);
e.printStackTrace();
} else {
if (cb != null)
cb.onResult(true);
ParseRelation<ParseSession> relation = ParseUser.getCurrentUser().getRelation("sessions");
relation.add(session);
syncUser();
}
}
});
}
public static void addNewSession(Date date, String link, int successValue) {
final ParseSession session = new ParseSession();
session.put("date", date);
session.put("link", link);
session.put("successValue", successValue);
session.pinInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e != null)
e.printStackTrace();
else {
fetchSession(session, new ParseManager.OnResultCallback() {
#Override
public void onResult(boolean success) {
if (success) {
try {
session.unpin();
} catch (ParseException e) {
e.printStackTrace();
}
}
}
});
}
}
});
}
public interface OnResultCallback {
void onResult(boolean success);
}
For creating new Session with my parameters and uploading it I use addNewSession() method, and it displays in Parse dashboard correctly, they have columns for my fields (date, link, successValue) and are stored as the default Session object.
But when I try to load them from Parse to my client, it doesn't work. I load them with this method:
public static void getSessions(final OnResultCallback cb) {
ParseRelation<ParseSession> relation = ParseUser.getCurrentUser().getRelation("sessions");
ParseQuery<ParseSession> query = relation.getQuery();
query.setLimit(1000);
query.findInBackground(new FindCallback<ParseSession>() {
#Override
public void done(List<ParseSession> objects, ParseException e) {
if (e != null) {
e.printStackTrace();
if (cb != null)
cb.onResult(false);
} else {
cb.onResult(true);
}
//if (objects != null)
//USE SESSIONS
}
});
}
I catch an exception:
W/System.err: com.parse.ParseRequest$ParseRequestException: wrong type of relation. Expecting: , but received: _Session
W/System.err: at com.parse.ParseRequest.newPermanentException(ParseRequest.java:270)
Quite similar code on IOS works fine with "sessions" relation. What mistakes have I done?
UPD. I have noticed that I get this exception only when I have allready send some custom created Session to Parse with addNewSession() and then trying to get them using getSessions(). Maybe creating and sending the Session is the problem?
I found the sollution. After I add my newly created Session to User relations with
ParseRelation<ParseSession> relation = ParseUser.getCurrentUser().getRelation("sessions");
relation.add(session);
I tried to save them with fetchInBackground, which was a mistake. I changed syncUser() to ParseUser.getCurrentUser().saveInBackground() and everything worked.

Invoke sendMessage method from second activity

I'm doing some dev for a Chromecast sender app, and I have this method in my MainActivity class:
public void sendMessage(String message) {
if (mApiClient != null && mHelloWorldChannel != null) {
try {
Cast.CastApi.sendMessage(mApiClient, mHelloWorldChannel.getNamespace(), message)
.setResultCallback(
new ResultCallback<Status>() {
#Override
public void onResult(Status result) {
if (!result.isSuccess()) {
Log.e(TAG, "Sending message failed");
} else {
System.out.println("Message sent!");
}
}
});
} catch (Exception e) {
Log.e(TAG, "Exception while sending message", e);
}
} else {
if (mApiClient == null) {
System.out.println("apiClient null");
}
if (mHelloWorldChannel == null) {
System.out.println("mHello null");
}
}
}
Every works dandy and I can send messages to my Chromecast and do nifty things with them when they get there. However, I use a number of other Activities in my app, and I'd like to be able to send messages to the Chromecast from those as well.
That said, what is the best way to access this method from a second activity?
If you will define your method static one then you can call it like this:
ClassName.sendMessage(String message);
If it is not static one, you will need to create an object for it.
ClassName c= new ClassName(SomeconstructorParams);
c.sendMessage(String message);

Can't send or receive presence with ASmack

I am trying to develop a XMPP chat client for Android (using Java connected to C#/Unity). I have got the Java -> Unity/C# connection working perfectly. I have also downloaded Asmack and can generate a library and my of wrapper class for initilazing a connection to a OpenFire XMPP server. However, I cannot seem to get Presence sent or recieved. I can log in, register new user and populate their roster, send messages, but cannot send any presence.
The code auto registrate users who never have used the app before. It also uses a preset friend list, and auto populates the roster with these friends.
The code is as follow (sorry for all the debug lines, can't use breakpoints when using Unity):
public class ASmackWrapper
{
private XMPPConnection connection;
private String[] friends;
private static final String eventClass = "ASmackEventListener";
private static ASmackWrapper wrapper;
public static ASmackWrapper instance()
{
System.out.println("instancecreator of ASmackWrapper 1!");
if (wrapper == null)
wrapper = new ASmackWrapper();
return wrapper;
}
public ASmackWrapper()
{
System.out.println("constructor of ASmackWrapper");
}
public boolean tryToRegister(String user, String pass){
AccountManager acManager = connection.getAccountManager();
try {
Map<String, String> attributes = new HashMap<String,String>();
attributes.put("email", "MY email");
acManager.createAccount(user, pass,attributes);
} catch (XMPPException e) {
System.out.println("cant autoregister user "+ user +" ... with pass: "+pass+" on server. error:" + e.getLocalizedMessage());
if (e.getLocalizedMessage().contains("conflict"))
return false; // Wrong password, since there is already an account with that id!
return false;
}
return true;
}
public void setFriends(String[] _friends) {
friends = _friends;
}
public void start(String host, String user, String pass)
{
System.out.println("Java: openConenction host:"+host);
ConnectionConfiguration cc = new ConnectionConfiguration(host,5222);
//cc.setSendPresence(true);
this.connection = new XMPPConnection(cc);
Connection.DEBUG_ENABLED = true;
try {
this.connection.connect();
} catch (XMPPException e) {
System.out.println("Error connecting to server");
return;
}
if(!this.connection.isConnected()) {
System.out.println("Java: is not connected");
onError("Connection failed");
return;
}
boolean loginStatus = login(user, pass);
if (!loginStatus) {
onError("Login Failed");
return;
}
RosterListener rl = new RosterListener() {
public void entriesAdded(Collection<String> addresses) {}
public void entriesUpdated(Collection<String> addresses) {}
public void entriesDeleted(Collection<String> addresses) {}
public void presenceChanged(Presence presence) {
System.out.println("presence changed!" + presence.getFrom() + " "+presence.getStatus());
onPresence(presence);
}
};
if (connection.getRoster() != null) {
connection.getRoster().setSubscriptionMode(Roster.SubscriptionMode.accept_all);
System.out.println("7");
connection.getRoster().addRosterListener(rl);
}
onAuthenticate("");
System.out.println("10");
//Set presence to online!
Presence presence = new Presence(Presence.Type.available);
presence.setStatus("Online, Programmatically!");
presence.setPriority(24);
presence.setMode(Presence.Mode.available);
connection.sendPacket(presence);
}
private void addFriends() throws Exception {
if (friends == null) {
System.out.println("No friends to add");
return;
}
System.out.println("Number of friends to add: "+friends.length);
for (int i = 0;i<friends.length;i++) {
System.out.println("Create user in roster: "+friends[i]);
connection.getRoster().createEntry("fb"+friends[i], "No name_",null);
}
}
private boolean login(String jid, String password) {
System.out.println("1");
boolean isLoggedIn=true;
try {
this.connection.login(jid, password);
} catch (XMPPException e) {
isLoggedIn=false;
}
System.out.println("2");
if(!isLoggedIn) {
boolean isRegistred = tryToRegister(jid,password);
if (isRegistred) {
connection.disconnect();
try {
connection.connect();
connection.login(jid, password);
} catch (XMPPException e) {
onError("Could not connect and login after registring");
return false;
}
} else {
return false;
}
}
try {
addFriends();
} catch (Exception e) {
onError("Could not add friends to roster");
}
ChatManager chatmanager = connection.getChatManager();
chatmanager.addChatListener(new ChatManagerListener()
{
public void chatCreated(final Chat chat, final boolean createdLocally)
{
System.out.println("OK Chat created!");
chat.addMessageListener(new MessageListener()
{
public void processMessage(Chat chat, Message message)
{
onMessage(chat, message);
}
});
}
});
return true;
}
public void sendMessage(String rec, String message) {
System.out.println("sendMessage(string,string) to host :"+connection.getHost());
Chat chat = connection.getChatManager().createChat(rec+"#"+connection.getHost(), new MessageListener() {
public void processMessage(Chat chat, Message message) {
// Print out any messages we get back to standard out.
System.out.println("Probably an error, since we got a instant reply on sent message. Received message body: " + message.getBody() + " from:"+message.getFrom() + " to:"+message.getTo());
}
});
try {
chat.sendMessage(message);
System.out.println("Message sent");
} catch (XMPPException e) {
System.out.println("Error sending message: "+e.toString());
e.printStackTrace();
}
}
public void logout () {
System.out.println("Login out...");
connection.disconnect();
}
public void getOnlineFriends() {
Roster roster = connection.getRoster();
Collection<RosterEntry> entries = roster.getEntries();
for(RosterEntry rosterEntry: entries) {
String user = rosterEntry.getUser();
Presence presence = roster.getPresence(user);
System.out.println("Presence : "+presence);
System.out.println("Presence type: "+presence.getType());
System.out.println("Presence mode: "+presence.getMode());
}
//Set presence to online!
Presence presence = new Presence(Presence.Type.available);
presence.setStatus("Online, Programmatically!");
presence.setPriority(24);
presence.setMode(Presence.Mode.available);
connection.sendPacket(presence);
}
private void onMessage(Chat chat, Message message) {
String m = ("Received message: " + (message != null ? message.getBody() : "NULL"));
System.out.println(m);
UnityPlayer.UnitySendMessage(eventClass, "Message", m);
}
private void onError(String message) {
UnityPlayer.UnitySendMessage(eventClass, "Error", message);
}
private void onAuthenticate(String message) {
UnityPlayer.UnitySendMessage(eventClass, "Authenticate", message);
}
private void onPresence(Presence presence) {
String user = presence.getFrom();
if (presence.getType() == Presence.Type.available)
UnityPlayer.UnitySendMessage(eventClass, "Online", user);
else
UnityPlayer.UnitySendMessage(eventClass, "Offline", user);
System.out.println("Java: Presence changed, from:" +presence.getFrom() + " type:"+presence.getType() + " toString:"+presence.toString());
}
}
The presence is checked in two ways, by setting a presence listener and by fetching the status after login. This SO page suggest waiting 5 sec before getting the precense: Unable to get presence of roster by using smack, openfire
I have tried that as well by calling getOnlineFriends from a button, more than 5 sec after login. The listener never gets called. It just fires once after login, once for each on the roster with a presence = null.
Edit: After turning on Debug mode on Asmack I see the following reply to my presence send message:
SENT <presence id="3sG7l-11"><status>Online, Programmatically!</status><priority>24</priority></presence>
RCV <presence id="6s7BX-5" to="787122012#xxx.tripnet.se/Smack" from="10000063946242" type="error">
<error code="404" type="cancel">
<remote-server-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
</error></presence>
The server log:
org.jivesoftware.openfire.nio.ConnectionHandler - Closing connection due to error while processing message:
<iq id="566-4" type="error" from="xxx.tripnet.se/f55aea72" to="xxx.tripnet.se">
<error /><error type="cancel" code="501">
<feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /></error>
<ping xmlns="urn:xmpp:ping" /></iq>
java.lang.IllegalArgumentException: IQ must be of type 'set' or 'get'. Original IQ:
<iq id="566-4" type="error" from="xxx.tripnet.se/f55aea72" to="xxx.tripnet.se">
<error/><error type="cancel" code="501">
<feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error>
<ping xmlns="urn:xmpp:ping"/></iq>
at org.xmpp.packet.IQ.createResultIQ(IQ.java:384)
I have also tried to pass along setSendPresence = true to the connectionconfiguration passed in connect(), but no difference.
I have also tried to set the subscription mode manually in OpenFire server to "both" (from "none") on both users, but with no effect.
Got it working! It was probably due to that I did not use the correct format on my jid's in the roster list. The correct format had to be user#myserver.se, not just user.
Write a RosterListener and see if it works.
public void rosterOnlineStatus(){
Roster roster = connection.getRoster();
Presence status = new Presence(Type.available);
status.setStatus("Hello This is Phaneendra");
roster.addRosterListener(new RosterListener() {
#Override
public void presenceChanged(Presence presence) {
System.out.println(presence.getFrom()+ "is "+presence+" "+presence.getStatus());
presence.getStatus();
}
#Override
public void entriesUpdated(Collection<String> addresses) {}
#Override
public void entriesDeleted(Collection<String> addresses) {}
#Override
public void entriesAdded(Collection<String> addresses) {}
});
}
See if adding the RosterListener will work?

Categories

Resources