2013년 8월 12일 월요일

asmack(smack android library) 사용 시 참고 사항들

이전 블로그에서 이전 함 (원본 글 2013/08/12 작성)

asmack을 사용하다 생긴 사소한 사항들을 정리


[reconnect 상황에서 null pointer exception 발생 현상]

= 증상 설명
# precondition
  : XMPP connection이 기타 사유(network connection close, 동일 id로 타 deivce에서 login 등)으로 close 됨
  : MUC(Multi User Chat) extension을 사용함.

 - XMPP connection을 재 생성하고자 connect를 할 경우 아래와 같이 NullPointerException이 발생

08-12 18:57:36.783: W/System.err(598): java.lang.NullPointerException
08-12 18:57:36.783: W/System.err(598):  at org.jivesoftware.smackx.muc.MultiUserChat$1.connectionCreated(MultiUserChat.java:115)
08-12 18:57:36.783: W/System.err(598):  at org.jivesoftware.smack.XMPPConnection.initConnection(XMPPConnection.java:663)
08-12 18:57:36.783: W/System.err(598):  at org.jivesoftware.smack.XMPPConnection.connectUsingConfiguration(XMPPConnection.java:615)
08-12 18:57:36.783: W/System.err(598):  at org.jivesoftware.smack.XMPPConnection.connect(XMPPConnection.java:1034)

자세한 원인은 모르겠지만 여러번 테스트와 약간의 소스를 확인한 결과 
잘못된 connect 루틴을 사용한 사소한 문제로 발생된 것으로 판단.

Smack에서 제공하는 connect의 sample 예제는 다음과 같다.
일반적으로 connection configuration을 만들고 이를 사용하여 connect를 하고 login을 하게 됨.

 // Create the configuration for this new connection
ConnectionConfiguration config = new ConnectionConfiguration("jabber.org", 5222); config.setCompressionEnabled(true); config.setSASLAuthenticationEnabled(true); Connection connection = new XMPPConnection(config); // Connect to the server connection.connect(); // Log into the server connection.login("username", "password", "SomeResource"); .... // Disconnect from the server connection.disconnect();

하지만 이를 reconnect 상황에서 그래도 사용할 경우 위와 같은 현상이 발생하는 것으로 보이고
로그상에서의 표면적인 원인은 MultiUserChat에서 사용하는 ServiceDiscoveryManager의 instance가 null이라 발생함.
더 세부적인 원인으로는 새롭게 생성하려는 XMPPConnection이 network 상황들로 인해 제대로 configuration이 되지 않은 상태에서 connection을 시도하여 발생한 것으로 그냥 추측만 할 뿐.. 자세한 내용을 알려면 XMPP spec 과 smack initialize 코드를 봐야 함. ㅜㅜ 나중에...

그래서 임시적인 solution으로는 
reconnect 상황에서 connection이 null 이 아닌 경우에만 connection을 생성하여 NullPointerException을 방지하고
불필요한 login도 줄여서 reconnect 함. 대충 아래와 같이?

 if(null == conn_){
connection = new XMPPConnection(config);
}
connection.connect();
if(false == conn_.isAuthenticated()){
connection.login(_info_.getXmppId(), password);
}


** 추가로 재접속 시에 connection이 끊겼다고 해서 굳이 disconnect를 할 필요가 없다.
smack에서는 자동적으로 connection을 timout을 조정하면서 재 연결하려는 시도를 하고 있고 이를 좀 더 즉각적으로 하기 위해서는 connect를 명시적으로 호출할 경우 재접속 시도는 cancel되고 바로 connect를 시도하게 된다. 
이 때 disconnect를 호출하게되면 재접속 시도를 명시적으로 cancel하게 하는 것이라 완전히 종료를 위해서만 사용하는게 맞겠음.

 Connections can be reused between connections. This means that an Connection may be connected, disconnected and then connected again. Listeners of the Connection will be retained accross connections.

If a connected Connection gets disconnected abruptly then it will try to reconnect again. To stop the reconnection process, use disconnect(). Once stopped you can use connect() to manually connect to the server.

 By default Smack will try to reconnect the connection in case it was abruptly disconnected. Use ConnectionConfiguration#setReconnectionAllowed(boolean) to turn on/off this feature. The reconnection manager will try to immediately reconnect to the server and increase the delay between attempts as successive reconnections keep failing. 
In case you want to force a reconnection while the reconnetion manager is waiting for the next reconnection, you can just use Connection#connect() and a new attempt will be made. If the manual attempt also failed then the reconnection manager will still continue the reconnection job.



[Multi User Chat extension을 사용하여 채팅 시 메시지 중복 수신 현상]

# precondition
  : XMPP connection이 close 된 후 재 연결된 상태
  : MUC(Multi User Chat) extension을 사용하여 채팅 중

- 사용자가 전송한 메세지가 중복으로 수신되는 현상

좀 바보같은 이유때문에 발생한 현상이지만 그래도 XMPP spec과 smack library의 특성으로 인해 충분히 발생할 상황이라 정리

multiuser chat에서의 join 예제를 보면 다음과 같고 
// Create a MultiUserChat using a Connection for a room
<pre style="font-family: 'courier new', monospaced;">      MultiUserChat muc2 = new MultiUserChat(conn1, "myroom@conference.jabber.org");

      // User2 joins the new room using a password
      // The room service will decide the amount of history to send
      muc2.join("testbot2", "password");</pre>
smack API reference에서는 join류 API들을 다음과 같이 설명하고 있음.
bold체로 되어있는 부분에서는 join을 호출하면 기존 muc room을 leave하고 join한다고 되어 있음.

 join
public void join(java.lang.String nickname) throws XMPPException

Joins the chat room using the specified nickname. If already joined using another nickname,this method will first leave the room and then re-join using the new nickname.The default timeout of Smack for a reply from the group chat server that the join succeeded will be used. After joining the room, the room will decide the amount of history to send.



<dl style="color: rgb(0, 0, 0); font-family: Gulim; font-size: medium; line-height: normal;">
Parameters:
nickname - the nickname to use.
Throws:
XMPPException - if an error occurs joining the room. In particular, a 401 error can occur if no password was provided and one is required; or a 403 error can occur if the user is banned; or a 404 error can occur if the room does not exist or is locked; or a 407 error can occur if user is not on the member list; or a 409 error can occur if someone is already in the group chat with the same nickname.
</dl>


하지만 asmack을 사용해서 테스트 해보니 좀 이상하다.
leave가 제대로 안되는 것인지는 모르겠지만 
connection이 재설정되는 상황에서 
 - re-join을 하지 않을 경우 메세지 송수신이 되지 않고
 - leave를 명시적으로 호출하지 않고 re-join을 하게 될 경우 메세지가 중복되어 수신된다.
 - leave를 명시적으로 호출하고 re-join을 하게 될 경우 메세지가 정상적으로 하나만 수신된다.

암튼 내가 잘못 코딩한것일 수 도 있을 수도 있으니 좀 더 테스트는 필요하겠음.



댓글 없음:

댓글 쓰기