Android Smack giving timeoutexception when joining MultiUserChat - android

I am trying to join a MultiUserChat using Smack on Android. Currently I can chat 1-on-1 perfectly fine, and I am connected to the server as I show online. I followed the examples provided here.
I have the following code to join a MultiUserChat (MUC).
final XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
.setUsernameAndPassword(user.getUsername(), user.getJabberPassword())
.setServiceName("app.buur.nu")
.setHost("app.buur.nu")
.setPort(5222)
.build();
AbstractXMPPConnection connection = new XMPPTCPConnection(config);
String room = "testroom";
MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);
MultiUserChat muc = manager.getMultiUserChat(room + "#groups.app.buur.nu");
try {
muc.join(user.getUsername(), null, null, connection.getPacketReplyTimeout());
} catch (SmackException.NoResponseException e) {
e.printStackTrace();
} catch (XMPPException.XMPPErrorException e) {
e.printStackTrace();
} catch (SmackException.NotConnectedException e) {
e.printStackTrace();
}
But this gives me
org.jivesoftware.smack.SmackException$NoResponseException: No response received within reply timeout. Timeout was 5000ms (~5s). Used filter: AndFilter: (FromMatchesFilter (full): testroom#groups.app.buur.nu/test, StanzaTypeFilter: org.jivesoftware.smack.packet.Presence).
I tried increasing the timeout to 10000 ms, but I still get a timeout. What could be wrong here? Creating 1-on-1 chats works fine and connection.isConnected()) returns True...
So it turns out that I get an error
<presence to="app.buur.nu/7c65be6" id="lgcSp-4" type="error"><x xmlns="http://jabber.org/protocol/muc"/><c xmlns="http://jabber.org/protocol/caps" hash="sha-1" node="http://www.igniterealtime.org/projects/smack" ver="os2Kusj3WEOivn5n4iFr/ZEO8ls="/><error code="401" type="auth"><not-authorized xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></presence>
Basically, the authentication is not completed when I am attempting to join the room. Can a listener be added to receive an update when the authentication has been completed? I saw https://www.igniterealtime.org/builds/smack/docs/latest/javadoc/org/jivesoftware/smack/SASLAuthentication.html#authenticate%28java.lang.String,%20javax.security.auth.callback.CallbackHandler%29 but implementing my own authentication mechanism seems a little overkill...
Isn't there a onAuthenticationCompletedListener or something?

It turns out there is no need to implement the SASLMechanism, you can do the following:
connection.addConnectionListener(new ConnectionListener() {
#Override
public void connected(XMPPConnection connection) {
}
#Override
public void authenticated(XMPPConnection connection, boolean resumed) {
joinMUCRooms();
}
#Override
public void connectionClosed() {
}
#Override
public void connectionClosedOnError(Exception e) {
}
#Override
public void reconnectionSuccessful() {
}
#Override
public void reconnectingIn(int seconds) {
}
#Override
public void reconnectionFailed(Exception e) {
}
});
The error no longer shows now, while keeping the code "fairly" clean.

Does the room exist? If not then you need to create it first, using create() and sending a instant form. You should also report to the openfire developers that the MUC error presence is missing the 'from' attribute.

Related

Using RxJava with Paho MQTT

In my Android app, I have a service which has an instance of a class(call it MQTTClient) which publishes or subscribes to an MQTT server. I want to use RxJava with Eclipse Paho Android to manage MQTT subscribe and publish operations.
I am using Single observable and SingleObserver for publishing, and Flowable observable and Observer for subscribing. But I am stuck at a point where I cannot figure out when and how to dispose of the Disposable.
Here is the Single Observable from the publish method in MQTTClient
Single<IMqttToken> pubTokenSingle = Single.create(new SingleOnSubscribe<IMqttToken>() {
#Override
public void subscribe(final SingleEmitter<IMqttToken> emitter) throws Exception {
final IMqttToken token = client.publish(topic, mqttMessage);
token.setActionCallback(new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
emitter.onSuccess(token);
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
boolean hasNetwork = isOnline(context);
if (hasNetwork && Objects.equals(((MqttException) exception).getReasonCode(),
MqttException.REASON_CODE_CLIENT_NOT_CONNECTED)) {
//connect client and retry MQTT pub
try {
//connect() is a method in MQTTClient
//connect() method also utilizes RxJava2 Single.
//Same issue of disposing a `Disposable` exists in that method as well
connect();
//call the publish method again
} catch (MqttException e) {
e.printStackTrace();
emitter.onError(e);
}
} else if (!hasNetwork) {
emitter.onError(exception);
} else {
emitter.onError(exception);
}
}
});
}
});
Here is the SingleObserver
final Disposable[] disposable = new Disposable[1];
SingleObserver<IMqttToken> pubTokenSingleObserver = new SingleObserver<IMqttToken>() {
#Override
public void onSubscribe(Disposable d) {
disposable[0] = d;
}
#Override
public void onSuccess(IMqttToken iMqttToken) {
//disposable[0].dispose();
//Planning to use the above as last resort
//Also thought of moving this to doOnSuccess
}
#Override
public void onError(Throwable e) {
//Put topic name, and mqtt message in SQLite
//disposable[0].dispose();
//Planning to use the above as last resort
//Also thought of moving this to doOnError
}
};
Someone suggested that I have a cleanup method in the concerned class which gets called when onStop is invoked.
I am concerned what would happen in case I use disposable.dispose() and the network operation is still in progress.
How do I ensure that if the operation is incomplete then at least the details persist in the SQLite DB?
I am hoping that the solution would be easily extensible for subscribing as well. If not then tell me about the possible pitfalls.
This is a learning project where I am learning RxJava2 that is why I didn't opt for RxMQTT.

Best way to keepAlive Connection With Xmpp Server in Android

I am working on chat application and using ejabberd saas edition as xmpp server for it. I am using smack library ver-4.2.3. To keep connection alive I am using ping manager. Here is the code I am using:
ReconnectionManager.getInstanceFor(AppController.mXmpptcpConnection).enableAutomaticReconnection();
ServerPingWithAlarmManager.onCreate(context);
ServerPingWithAlarmManager.getInstanceFor(AppController.mXmpptcpConnection).setEnabled(true);
ReconnectionManager.setEnabledPerDefault(true);
//int i = 1;
// PingManager.setDefaultPingInterval(i);
PingManager.getInstanceFor(AppController.mXmpptcpConnection).setPingInterval(300);
I am using sticky-service also for connection, but when I keep my application open (ideal-state) for 15-20 mins then the connection is lost, so I am using ping manger to resolve this issue.
Is there any other better way of doing it or ping manager is the only option?
Insted of pinging chat server constantly, you better to use ConnectionListener() in smack library. You need to use something like this:
XMPPTCPConnection connection;
// initialize your connection
// handle the connection
connection.addConnectionListener(new ConnectionListener() {
#Override
public void connected(XMPPConnection connection) {
}
#Override
public void authenticated(XMPPConnection connection, boolean resumed) {
}
#Override
public void connectionClosed() {
// when the connection is closed, try to reconnect to the server.
}
#Override
public void connectionClosedOnError(Exception e) {
// when the connection is closed, try to reconnect to the server.
}
#Override
public void reconnectionSuccessful() {
}
#Override
public void reconnectingIn(int seconds) {
}
#Override
public void reconnectionFailed(Exception e) {
// do something here, did you want to reconnect or send the error message?
}
});
Best way to keep the alive connection with XMPP server you should reconnect after every network change.
Like this:
public class NetworkStateChangeReceiver extends BroadcastReceiver {
private Context context;
private static NetworkStateChangeListener mListener;
#Override
public void onReceive(Context context, Intent intent) {
this.context = context;
try {
if (!ApplicationHelper.isInternetOn(context)) {
if (mListener != null) {
mListener.OnInternetStateOff();
}
return;
} else {
XMPPTCPConnection xmpptcpConnection = XmppConnectionHelper.getConnection();
if(!StringHelper.isNullOrEmpty(new SessionManager(context).getAuthenticationToken())) {
Intent XmppConnectionServicesIntent = new Intent(context, XmppConnectionServices.class);
context.stopService(XmppConnectionServicesIntent);
context.startService(XmppConnectionServicesIntent);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
//to initialize NetworkStateChangeListener because null pointer exception occurred
public static void setNetworkStateChangeListener(NetworkStateChangeListener listener) {
mListener = listener;
}
}
Yes, There is. Few points before the solution
Make your service STICKY, with a foreground notification as it would be necessary to work on or after Build.VERSION_CODES.O
This sticky service, you should start on every boot, via BOOT_COMPLETED intent action and starting this foreground service from receiver.
Yes, Now it is always there, Now you can always go for checking your connection
You can use google-volley for making connections and even you can communicate using it.
There is no good documentation on it, But i like it much, as it works flawlessly once added the dependency successfully.
Adding this dependency will take time as i said no good documentation..
For communication :
StringRequest stringRequest = new StringRequest(Request.Method.POST, "https://oniony-leg.000webhostapp.com/user_validation.php",
new Response.Listener<String>()
{
#Override
public void onResponse(String response)
{
serverKeyResponse = response;
// get full table entries from below toast and writedb LICENSETABLE
//Toast.makeText(getActivity(),response,Toast.LENGTH_LONG).show();
showKeyResponse();
// Log.d("XXXXXX XXXXX", "\n SUCCESS : "+serverKeyResponse);
}
},
new Response.ErrorListener()
{
#Override
public void onErrorResponse(VolleyError error)
{
serverKeyResponse = error.toString();
// show below toast in alert dialog and it happens on slow internet try again after few minutes
// on ok exit app
// Toast.makeText(getActivity(),error.toString(),Toast.LENGTH_LONG).show();
showKeyResponse();
//Log.d("YYYYYY YYYYYY", "\n FAILURE : "+serverKeyResponse);
}
})
{
#Override
protected Map<String,String> getParams()
{
Map<String,String> params = new HashMap<String, String>();
params.put("INPUT",LicenseKey.getText().toString());
params.put("USER", MainActivity.deviceid);
return params;
}
};
RequestQueue requestQueue = Volley.newRequestQueue(getActivity());
requestQueue.add(stringRequest);
You just have to reply ECHO "SUCCESS" from server using a php ( or whatever server side language you like ). In response check for SUCCESS presence, any any other cases.., Use other KEYWORDS YOU LIKE. You can handle Server response errors too. Even you can communicate from android in request - response handshake. But you have to implement few handshake on your own.
I Hope, It helps...
Use the ReconnectionManager class as described here.
ReconnectionManager manager = ReconnectionManager.getInstanceFor(connection);
manager.enableAutomaticReconnection();
It will automatically re-connect when necessary.

How do you listen for authentication errors using JavaMail API?

I am writing a simple javamail client to send emails from an account. I want to listen for authentication failures, so I can display an appropriate message. I am trying to use the Transport object and add a connection listener to it. I can see in the console I am getting errors but the object I am assigning to "addConnectionListener()" is not picking it up. Sample code:
mSession = Session.getInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(CustomMailSender.this.mUser, CustomMailSender.this.mPassword);
}
});
mTransport = mSession.getTransport(protocol);
mTransport.addConnectionListener(new ConnectionListener() {
#Override
public void opened(ConnectionEvent e) {
Timber.e(" connection opened");
}
#Override
public void disconnected(ConnectionEvent e) {
Timber.e(" connection disconnected");
}
#Override
public void closed(ConnectionEvent e) {
Timber.e(" connection closed");
}
});
int iPort = Integer.parseInt(port);
mTransport.connect(mMailHost,iPort , user, password);
I am sending the message here
mTransport.sendMessage(message, InternetAddress.parse(recipients));
I had expected that if an attempt to connect fails, that the "disconnected" even would trigger ?
In JavaMail, "connected" really means "connected and authenticated and ready for use". If authentication fails, the Transport is never "connected".
I'm not sure why you need to "listen" for authentication failures since they'll be reported synchronously when the connect method throws AuthenticationFailedException.

XMPP “stream:error (conflict)” when try to reconnect or login

I am using Smack and Openfire server for a chat client, all things working well like chat, sending an invitation for a new addition of user, getting list of available users etc. I don't have a clue what to do if the connection is in a Sticky Service and I added a connection listener to the connection and connection disconnected, let's say for "Internet Connection"
I am using the below code for the connectionlistener.
connection.addConnectionListener(new ConnectionListener() {
#Override
public void reconnectionSuccessful() {
Log.i("","Successfully reconnected to the XMPP server.");
}
#Override
public void reconnectionFailed(Exception arg0) {
Log.i("","Failed to reconnect to the XMPP server.");
}
#Override
public void reconnectingIn(int seconds) {
Log.i("","Reconnecting in " + seconds + " seconds.");
}
#Override
public void connectionClosedOnError(Exception arg0) {
Log.i("","Connection to XMPP server was lost.");
Log.v("ONMESSAGE", "Error was " + arg0.toString() + "and Now connecting");
}
#Override
public void connectionClosed() {
Log.i("","XMPP connection was closed.");
}
});
So I thought of adding two lines of code to connectionClosedOnError() i.e
connection.disconnect(new Presence(Presence.Type.unavailable));
//code for connection(new one)
Which gives me some time following error
No Response from Server.
Not connected to server.
conflict error
Now I researched on the issue and found there is still connection when I try to reconnect using the same resource so I got the errors. My question is how to do reconnection and what is the correct procedure for that?
I know how to resolve the issue "XMPP “stream:error (conflict)” as I can provide a String to login() method as a third parameter and it resolves the issue.
That is not my main concern, what I want to know the procedure of reconnection. I tried logging in all methods and it's quite amazing that there are no order of methods being called.

What happened to Packet.setProperty() in (a)Smack 4.0?

Hi i am recently converting from aSmack 3.X to aSmack 4.0. In aSmack 4.0 it seems that the Packet.setProperty() method has been removed. Is there any documentation or examples of how to get this setting to the packet back in aSmack 4.0 as my system relies heavily on aSmack properties.
Thank you in advance.
Those methods where moved out of smack-core into smack-extensions with Smack 4. You now need to use JivePropertiesManager.getProperty(Packet, String) and JivePropertiesManager.addProperty(Packet, String) to add and get those.
More info about the API changes from Smack 3 to Smack 4 can be found at the official "Smack 4.0 Readme and Upgrade Guide".
This is an upgrade of my last FCM XMPP Connection Server application. Now, this project uses the latest version at this time of the Smack library (4.1.8).
https://github.com/carlosCharz/fcmxmppserverv2
This is my sample java project to showcase the Firebase Cloud Messaging (FCM) XMPP Connection Server. This server sends data to a client app via the FCM CCS Server using the XMPP protocol.
https://github.com/carlosCharz/fcmxmppserver
And also I've created a video in youtube where I explain what the changes are.
https://www.youtube.com/watch?v=KVKEj6PeLTc
Hope you find it useful.
Here is a code snippet for the implementation:
public class CcsClient implements StanzaListener {
//Other code
public void connect() throws XMPPException, SmackException, IOException {
XMPPTCPConnection.setUseStreamManagementResumptionDefault(true);
XMPPTCPConnection.setUseStreamManagementDefault(true);
XMPPTCPConnectionConfiguration.Builder config = XMPPTCPConnectionConfiguration.builder();
config.setServiceName("FCM XMPP Client Connection Server");
config.setHost(Util.FCM_SERVER);
config.setPort(Util.FCM_PORT);
config.setSecurityMode(SecurityMode.ifpossible);
config.setSendPresence(false);
config.setSocketFactory(SSLSocketFactory.getDefault());
// Launch a window with info about packets sent and received
config.setDebuggerEnabled(mDebuggable);
// Create the connection
connection = new XMPPTCPConnection(config.build());
// Connect
connection.connect();
// Enable automatic reconnection
ReconnectionManager.getInstanceFor(connection).enableAutomaticReconnection();
// Handle reconnection and connection errors
connection.addConnectionListener(new ConnectionListener() {
#Override
public void reconnectionSuccessful() {
logger.log(Level.INFO, "Reconnection successful ...");
// TODO: handle the reconnecting successful
}
#Override
public void reconnectionFailed(Exception e) {
logger.log(Level.INFO, "Reconnection failed: ", e.getMessage());
// TODO: handle the reconnection failed
}
#Override
public void reconnectingIn(int seconds) {
logger.log(Level.INFO, "Reconnecting in %d secs", seconds);
// TODO: handle the reconnecting in
}
#Override
public void connectionClosedOnError(Exception e) {
logger.log(Level.INFO, "Connection closed on error");
// TODO: handle the connection closed on error
}
#Override
public void connectionClosed() {
logger.log(Level.INFO, "Connection closed");
// TODO: handle the connection closed
}
#Override
public void authenticated(XMPPConnection arg0, boolean arg1) {
logger.log(Level.INFO, "User authenticated");
// TODO: handle the authentication
}
#Override
public void connected(XMPPConnection arg0) {
logger.log(Level.INFO, "Connection established");
// TODO: handle the connection
}
});
// Handle incoming packets (the class implements the StanzaListener)
connection.addAsyncStanzaListener(this,
stanza -> stanza.hasExtension(Util.FCM_ELEMENT_NAME, Util.FCM_NAMESPACE));
// Log all outgoing packets
connection.addPacketInterceptor(stanza -> logger.log(Level.INFO, "Sent: {}", stanza.toXML()), stanza -> true);
connection.login(fcmServerUsername, mApiKey);
logger.log(Level.INFO, "Logged in: " + fcmServerUsername);
}\
}

Categories

Resources