PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/java/org/opensrf/net/xmpp/XMPPSession.java

https://gitlab.com/evergreen-bjwebb/opensrf-debian
Java | 263 lines | 137 code | 46 blank | 80 comment | 22 complexity | 472d01326275bc38d8b2f8f81664f10e MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause
  1. package org.opensrf.net.xmpp;
  2. import java.io.*;
  3. import java.net.Socket;
  4. import java.util.Map;
  5. import java.util.Iterator;
  6. import java.util.concurrent.ConcurrentHashMap;
  7. /**
  8. * Represents a single XMPP session. Sessions are responsible for writing to
  9. * the stream and for managing a stream reader.
  10. */
  11. public class XMPPSession {
  12. /** Initial jabber message */
  13. public static final String JABBER_CONNECT =
  14. "<stream:stream to='%s' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>";
  15. /** Basic auth message */
  16. public static final String JABBER_BASIC_AUTH =
  17. "<iq id='123' type='set'><query xmlns='jabber:iq:auth'>" +
  18. "<username>%s</username><password>%s</password><resource>%s</resource></query></iq>";
  19. public static final String JABBER_DISCONNECT = "</stream:stream>";
  20. private static Map threadConnections = new ConcurrentHashMap();
  21. /** jabber domain */
  22. private String host;
  23. /** jabber port */
  24. private int port;
  25. /** jabber username */
  26. private String username;
  27. /** jabber password */
  28. private String password;
  29. /** jabber resource */
  30. private String resource;
  31. /** XMPP stream reader */
  32. XMPPReader reader;
  33. /** Fprint-capable socket writer */
  34. PrintWriter writer;
  35. /** Raw socket output stream */
  36. OutputStream outStream;
  37. /** The raw socket */
  38. Socket socket;
  39. /** The process-wide session. All communication occurs
  40. * accross this single connection */
  41. private static XMPPSession globalSession;
  42. /**
  43. * Creates a new session.
  44. * @param host The jabber domain
  45. * @param port The jabber port
  46. */
  47. public XMPPSession( String host, int port ) {
  48. this.host = host;
  49. this.port = port;
  50. }
  51. /**
  52. * Returns the global, process-wide session
  53. */
  54. /*
  55. public static XMPPSession getGlobalSession() {
  56. return globalSession;
  57. }
  58. */
  59. public static XMPPSession getThreadSession() {
  60. return (XMPPSession) threadConnections.get(new Long(Thread.currentThread().getId()));
  61. }
  62. /**
  63. * Sets the given session as the global session for the current thread
  64. * @param ses The session
  65. */
  66. public static void setThreadSession(XMPPSession ses) {
  67. /* every time we create a new connection, clean up any dead threads.
  68. * this is cheaper than cleaning up the dead threads at every access. */
  69. cleanupThreadSessions();
  70. threadConnections.put(new Long(Thread.currentThread().getId()), ses);
  71. }
  72. /**
  73. * Analyzes the threadSession data to see if there are any sessions
  74. * whose controlling thread has gone away.
  75. */
  76. private static void cleanupThreadSessions() {
  77. Thread threads[] = new Thread[Thread.activeCount()];
  78. Thread.enumerate(threads);
  79. for(Iterator i = threadConnections.keySet().iterator(); i.hasNext(); ) {
  80. boolean found = false;
  81. Long id = (Long) i.next();
  82. for(Thread t : threads) {
  83. if(t.getId() == id.longValue()) {
  84. found = true;
  85. break;
  86. }
  87. }
  88. if(!found)
  89. threadConnections.remove(id);
  90. }
  91. }
  92. /**
  93. * Sets the global, process-wide section
  94. */
  95. /*
  96. public static void setGlobalSession(XMPPSession ses) {
  97. globalSession = ses;
  98. }
  99. */
  100. /** true if this session is connected to the server */
  101. public boolean connected() {
  102. return (
  103. reader != null &&
  104. reader.getXMPPStreamState() == XMPPReader.XMPPStreamState.CONNECTED &&
  105. !socket.isClosed()
  106. );
  107. }
  108. /**
  109. * Connects to the network.
  110. * @param username The jabber username
  111. * @param password The jabber password
  112. * @param resource The Jabber resource
  113. */
  114. public void connect(String username, String password, String resource) throws XMPPException {
  115. this.username = username;
  116. this.password = password;
  117. this.resource = resource;
  118. try {
  119. /* open the socket and associated streams */
  120. socket = new Socket(host, port);
  121. /** the session maintains control over the output stream */
  122. outStream = socket.getOutputStream();
  123. writer = new PrintWriter(outStream, true);
  124. /** pass the input stream to the reader */
  125. reader = new XMPPReader(socket.getInputStream());
  126. } catch(IOException ioe) {
  127. throw new
  128. XMPPException("unable to communicate with host " + host + " on port " + port);
  129. }
  130. /* build the reader thread */
  131. Thread thread = new Thread(reader);
  132. thread.setDaemon(true);
  133. thread.start();
  134. synchronized(reader) {
  135. /* send the initial jabber message */
  136. sendConnect();
  137. reader.waitCoreEvent(10000);
  138. }
  139. if( reader.getXMPPStreamState() != XMPPReader.XMPPStreamState.CONNECT_RECV )
  140. throw new XMPPException("unable to connect to jabber server");
  141. synchronized(reader) {
  142. /* send the basic auth message */
  143. sendBasicAuth();
  144. reader.waitCoreEvent(10000);
  145. }
  146. if(!connected())
  147. throw new XMPPException("Authentication failed");
  148. }
  149. /** Sends the initial jabber message */
  150. private void sendConnect() {
  151. reader.setXMPPStreamState(XMPPReader.XMPPStreamState.CONNECT_SENT);
  152. writer.printf(JABBER_CONNECT, host);
  153. }
  154. /** Send the basic auth message */
  155. private void sendBasicAuth() {
  156. reader.setXMPPStreamState(XMPPReader.XMPPStreamState.AUTH_SENT);
  157. writer.printf(JABBER_BASIC_AUTH, username, password, resource);
  158. }
  159. /**
  160. * Sends an XMPPMessage.
  161. * @param msg The message to send.
  162. */
  163. public synchronized void send(XMPPMessage msg) throws XMPPException {
  164. checkConnected();
  165. try {
  166. String xml = msg.toXML();
  167. outStream.write(xml.getBytes());
  168. } catch (Exception e) {
  169. throw new XMPPException(e.toString());
  170. }
  171. }
  172. /**
  173. * @throws XMPPException if we are no longer connected.
  174. */
  175. private void checkConnected() throws XMPPException {
  176. if(!connected())
  177. throw new XMPPException("Disconnected stream");
  178. }
  179. /**
  180. * Receives messages from the network.
  181. * @param timeout Maximum number of milliseconds to wait for a message to arrive.
  182. * If timeout is negative, this method will wait indefinitely.
  183. * If timeout is 0, this method will not block at all, but will return a
  184. * message if there is already a message available.
  185. */
  186. public XMPPMessage recv(long timeout) throws XMPPException {
  187. XMPPMessage msg;
  188. if(timeout < 0) {
  189. while(true) { /* wait indefinitely for a message to arrive */
  190. reader.waitCoreEvent(timeout);
  191. msg = reader.popMessageQueue();
  192. if( msg != null ) return msg;
  193. checkConnected();
  194. }
  195. } else {
  196. while(timeout >= 0) { /* wait at most 'timeout' milleseconds for a message to arrive */
  197. msg = reader.popMessageQueue();
  198. if( msg != null ) return msg;
  199. timeout -= reader.waitCoreEvent(timeout);
  200. msg = reader.popMessageQueue();
  201. if( msg != null ) return msg;
  202. checkConnected();
  203. if(timeout == 0) break;
  204. }
  205. }
  206. return reader.popMessageQueue();
  207. }
  208. /**
  209. * Disconnects from the jabber server and closes the socket
  210. */
  211. public void disconnect() {
  212. try {
  213. outStream.write(JABBER_DISCONNECT.getBytes());
  214. socket.close();
  215. } catch(Exception e) {}
  216. }
  217. }