ce7e36c06c13e02806310f8eb0955aa0b4ffd342
[OpenSRF.git] / src / java / org / opensrf / net / xmpp / XMPPSession.java
1 package org.opensrf.net.xmpp;
2
3 import java.io.*;
4 import java.net.Socket;
5
6
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
13     /** Initial jabber message */
14     public static final String JABBER_CONNECT = 
15         "<stream:stream to='%s' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>";
16
17     /** Basic auth message */
18     public static final String JABBER_BASIC_AUTH =  
19         "<iq id='123' type='set'><query xmlns='jabber:iq:auth'>" +
20         "<username>%s</username><password>%s</password><resource>%s</resource></query></iq>";
21
22     /** jabber domain */
23     private String host;
24     /** jabber port */
25     private int port;
26     /** jabber username */
27     private String username;
28     /** jabber password */
29     private String password;
30     /** jabber resource */
31     private String resource;
32
33     /** XMPP stream reader */
34     XMPPReader reader;
35     /** Fprint-capable socket writer */
36     PrintWriter writer;
37     /** Raw socket output stream */
38     OutputStream outStream;
39
40     /** The process-wide session.  All communication occurs
41      * accross this single connection */
42     private static XMPPSession globalSession;
43
44
45     /**
46      * Creates a new session.
47      * @param host The jabber domain
48      * @param port The jabber port
49      */
50     public XMPPSession( String host, int port ) {
51         this.host = host;
52         this.port = port;
53     }
54
55     /**
56      * Returns the global, process-wide session
57      */
58     public static XMPPSession getGlobalSession() {
59         return globalSession;
60     }
61
62     /**
63      * Sets the global, process-wide section
64      */
65     public static void setGlobalSession(XMPPSession ses) {
66         globalSession = ses;
67     }
68
69
70     /** true if this session is connected to the server */
71     public boolean connected() {
72         return (
73             reader != null && 
74             reader.getXMPPStreamState() == 
75                 XMPPReader.XMPPStreamState.CONNECTED);
76     }
77
78
79     /**
80      * Connects to the network.
81      * @param username The jabber username
82      * @param password The jabber password
83      * @param resource The Jabber resource
84      */
85     public void connect(String username, String password, String resource) throws XMPPException {
86
87         this.username = username;
88         this.password = password;
89         this.resource = resource;
90
91         Socket socket;
92
93         try { 
94             /* open the socket and associated streams */
95             socket = new Socket(host, port);
96
97             /** the session maintains control over the output stream */
98             outStream = socket.getOutputStream();
99             writer = new PrintWriter(outStream, true);
100
101             /** pass the input stream to the reader */
102             reader = new XMPPReader(socket.getInputStream());
103
104         } catch(IOException ioe) {
105             throw new 
106                 XMPPException("unable to communicate with host " + host + " on port " + port);
107         }
108
109         /* build the reader thread */
110         Thread thread = new Thread(reader);
111         thread.setDaemon(true);
112         thread.start();
113
114         /* send the initial jabber message */
115         sendConnect();
116         reader.waitCoreEvent(10000);
117         if( reader.getXMPPStreamState() != XMPPReader.XMPPStreamState.CONNECT_RECV ) 
118             throw new XMPPException("unable to connect to jabber server");
119
120         /* send the basic auth message */
121         sendBasicAuth(); /* XXX add support for other auth mechanisms */
122         reader.waitCoreEvent(10000);
123         if(!connected())
124             throw new XMPPException("Authentication failed");
125     }
126
127     /** Sends the initial jabber message */
128     private void sendConnect() {
129         writer.printf(JABBER_CONNECT, host);
130         reader.setXMPPStreamState(XMPPReader.XMPPStreamState.CONNECT_SENT);
131     }
132
133     /** Send the basic auth message */
134     private void sendBasicAuth() {
135         writer.printf(JABBER_BASIC_AUTH, username, password, resource);
136         reader.setXMPPStreamState(XMPPReader.XMPPStreamState.AUTH_SENT);
137     }
138
139
140     /**
141      * Sends an XMPPMessage.
142      * @param msg The message to send.
143      */
144     public synchronized void send(XMPPMessage msg) throws XMPPException {
145         checkConnected();
146         try {
147             String xml = msg.toXML();
148             outStream.write(xml.getBytes()); 
149         } catch (Exception e) {
150             throw new XMPPException(e.toString());
151         }
152     }
153
154
155     /**
156      * @throws XMPPException if we are no longer connected.
157      */
158     private void checkConnected() throws XMPPException {
159         if(!connected())
160             throw new XMPPException("Disconnected stream");
161     }
162
163
164     /**
165      * Receives messages from the network.  
166      * @param timeout Maximum number of milliseconds to wait for a message to arrive.
167      * If timeout is negative, this method will wait indefinitely.
168      * If timeout is 0, this method will not block at all, but will return a 
169      * message if there is already a message available.
170      */
171     public XMPPMessage recv(long timeout) throws XMPPException {
172
173         XMPPMessage msg;
174
175         if(timeout < 0) {
176
177             while(true) { /* wait indefinitely for a message to arrive */
178                 reader.waitCoreEvent(timeout);
179                 msg = reader.popMessageQueue();
180                 if( msg != null ) return msg;
181                 checkConnected();
182             }
183
184         } else {
185
186             while(timeout >= 0) { /* wait at most 'timeout' milleseconds for a message to arrive */
187                 timeout -= reader.waitCoreEvent(timeout);
188                 msg = reader.popMessageQueue();
189                 if( msg != null ) return msg;
190                 checkConnected();
191             }
192         }
193
194         return null;
195     }
196 }
197