KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jivesoftware > messenger > net > SocketConnection


1 /**
2  * $RCSfile: SocketConnection.java,v $
3  * $Revision: 1.24 $
4  * $Date: 2005/05/29 00:13:02 $
5  *
6  * Copyright (C) 2004 Jive Software. All rights reserved.
7  *
8  * This software is published under the terms of the GNU Public License (GPL),
9  * a copy of which is included in this distribution.
10  */

11
12 package org.jivesoftware.messenger.net;
13
14 import org.jivesoftware.messenger.*;
15 import org.jivesoftware.messenger.auth.UnauthorizedException;
16 import org.jivesoftware.messenger.interceptor.InterceptorManager;
17 import org.jivesoftware.messenger.interceptor.PacketRejectedException;
18 import org.jivesoftware.util.LocaleUtils;
19 import org.jivesoftware.util.Log;
20 import org.xmpp.packet.Packet;
21
22 import java.io.BufferedWriter JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.OutputStreamWriter JavaDoc;
25 import java.io.Writer JavaDoc;
26 import java.net.InetAddress JavaDoc;
27 import java.net.Socket JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.Map JavaDoc;
31
32 /**
33  * An object to track the state of a XMPP client-server session.
34  * Currently this class contains the socket channel connecting the
35  * client and server.
36  *
37  * @author Iain Shigeoka
38  */

39 public class SocketConnection implements Connection {
40
41     /**
42      * The utf-8 charset for decoding and encoding XMPP packet streams.
43      */

44     public static final String JavaDoc CHARSET = "UTF-8";
45
46     private Map JavaDoc listeners = new HashMap JavaDoc();
47
48     private Socket JavaDoc socket;
49
50     private Writer JavaDoc writer;
51
52     private PacketDeliverer deliverer;
53
54     private Session session;
55     private boolean secure;
56     private org.jivesoftware.util.XMLWriter xmlSerializer;
57     private boolean flashClient = false;
58     private int majorVersion = 1;
59     private int minorVersion = 0;
60     private String JavaDoc language = null;
61
62     /**
63      * Create a new session using the supplied socket.
64      *
65      * @param deliverer the packet deliverer this connection will use.
66      * @param socket the socket to represent.
67      * @param isSecure true if this is a secure connection.
68      * @throws NullPointerException if the socket is null.
69      */

70     public SocketConnection(PacketDeliverer deliverer, Socket JavaDoc socket, boolean isSecure)
71             throws IOException JavaDoc
72     {
73         if (socket == null) {
74             throw new NullPointerException JavaDoc("Socket channel must be non-null");
75         }
76
77         this.secure = isSecure;
78         this.socket = socket;
79         writer = new BufferedWriter JavaDoc(new OutputStreamWriter JavaDoc(socket.getOutputStream(), CHARSET));
80         this.deliverer = deliverer;
81         xmlSerializer = new XMLSocketWriter(writer, socket);
82     }
83
84     public boolean validate() {
85         if (isClosed()) {
86             return false;
87         }
88         try {
89             synchronized (writer) {
90                 // Register that we started sending data on the connection
91
SocketSendingTracker.getInstance().socketStartedSending(socket);
92                 writer.write(" ");
93                 writer.flush();
94             }
95         }
96         catch (Exception JavaDoc e) {
97             Log.warn("Closing no longer valid connection" + "\n" + this.toString(), e);
98             close();
99         }
100         finally {
101             // Register that we finished sending data on the connection
102
SocketSendingTracker.getInstance().socketFinishedSending(socket);
103         }
104         return !isClosed();
105     }
106
107     public void init(Session owner) {
108         session = owner;
109     }
110
111     public Object JavaDoc registerCloseListener(ConnectionCloseListener listener, Object JavaDoc handbackMessage) {
112         Object JavaDoc status = null;
113         if (isClosed()) {
114             listener.onConnectionClose(handbackMessage);
115         }
116         else {
117             status = listeners.put(listener, handbackMessage);
118         }
119         return status;
120     }
121
122     public Object JavaDoc removeCloseListener(ConnectionCloseListener listener) {
123         return listeners.remove(listener);
124     }
125
126     public InetAddress JavaDoc getInetAddress() {
127         return socket.getInetAddress();
128     }
129
130     public Writer JavaDoc getWriter() {
131         return writer;
132     }
133
134     public boolean isClosed() {
135         if (session == null) {
136             return socket.isClosed();
137         }
138         return session.getStatus() == Session.STATUS_CLOSED;
139     }
140
141     public boolean isSecure() {
142         return secure;
143     }
144
145     public int getMajorXMPPVersion() {
146         return majorVersion;
147     }
148
149     public int getMinorXMPPVersion() {
150         return minorVersion;
151     }
152
153     /**
154      * Sets the XMPP version information. In most cases, the version should be "1.0".
155      * However, older clients using the "Jabber" protocol do not set a version. In that
156      * case, the version is "0.0".
157      *
158      * @param majorVersion the major version.
159      * @param minorVersion the minor version.
160      */

161     public void setXMPPVersion(int majorVersion, int minorVersion) {
162         this.majorVersion = majorVersion;
163         this.minorVersion = minorVersion;
164     }
165
166     public String JavaDoc getLanguage() {
167         return language;
168     }
169
170     /**
171      * Sets the language code that should be used for this connection (e.g. "en").
172      *
173      * @param language the language code.
174      */

175     public void setLanaguage(String JavaDoc language) {
176         this.language = language;
177     }
178
179     public boolean isFlashClient() {
180         return flashClient;
181     }
182
183     /**
184      * Sets whether the connected client is a flash client. Flash clients need to
185      * receive a special character (i.e. \0) at the end of each xml packet. Flash
186      * clients may send the character \0 in incoming packets and may start a
187      * connection using another openning tag such as: "flash:client".
188      *
189      * @param flashClient true if the if the connection is a flash client.
190      */

191     public void setFlashClient(boolean flashClient) {
192         this.flashClient = flashClient;
193     }
194
195     public void close() {
196         boolean wasClosed = false;
197         synchronized (this) {
198             if (!isClosed()) {
199                 try {
200                     if (session != null) {
201                         session.setStatus(Session.STATUS_CLOSED);
202                     }
203                     synchronized (writer) {
204                         try {
205                             // Register that we started sending data on the connection
206
SocketSendingTracker.getInstance().socketStartedSending(socket);
207                             writer.write("</stream:stream>");
208                             if (flashClient) {
209                                 writer.write('\0');
210                             }
211                             writer.flush();
212                         }
213                         catch (IOException JavaDoc e) {}
214                         finally {
215                             // Register that we finished sending data on the connection
216
SocketSendingTracker.getInstance().socketFinishedSending(socket);
217                         }
218                     }
219                 }
220                 catch (Exception JavaDoc e) {
221                     Log.error(LocaleUtils.getLocalizedString("admin.error.close")
222                             + "\n" + this.toString(), e);
223                 }
224                 try {
225                     socket.close();
226                 }
227                 catch (Exception JavaDoc e) {
228                     Log.error(LocaleUtils.getLocalizedString("admin.error.close")
229                             + "\n" + this.toString(), e);
230                 }
231                 wasClosed = true;
232             }
233         }
234         if (wasClosed) {
235             notifyCloseListeners();
236         }
237     }
238
239     public void deliver(Packet packet) throws UnauthorizedException, PacketException {
240         if (isClosed()) {
241             deliverer.deliver(packet);
242         }
243         else {
244             try {
245                 // Invoke the interceptors before we send the packet
246
InterceptorManager.getInstance().invokeInterceptors(packet, session, false, false);
247                 boolean errorDelivering = false;
248                 synchronized (writer) {
249                     try {
250                         xmlSerializer.write(packet.getElement());
251                         if (flashClient) {
252                             writer.write('\0');
253                         }
254                         xmlSerializer.flush();
255                     }
256                     catch (IOException JavaDoc e) {
257                         Log.debug("Error delivering packet" + "\n" + this.toString(), e);
258                         errorDelivering = true;
259                     }
260                 }
261                 if (errorDelivering) {
262                     close();
263                     // Retry sending the packet again. Most probably if the packet is a
264
// Message it will be stored offline
265
deliverer.deliver(packet);
266                 }
267                 else {
268                     // Invoke the interceptors after we have sent the packet
269
InterceptorManager.getInstance().invokeInterceptors(packet, session, false, true);
270                     session.incrementServerPacketCount();
271                 }
272             }
273             catch (PacketRejectedException e) {
274                 // An interceptor rejected the packet so do nothing
275
}
276         }
277     }
278
279     public void deliverRawText(String JavaDoc text) {
280         if (!isClosed()) {
281             boolean errorDelivering = false;
282             synchronized (writer) {
283                 try {
284                     // Register that we started sending data on the connection
285
SocketSendingTracker.getInstance().socketStartedSending(socket);
286                     writer.write(text);
287                     if (flashClient) {
288                         writer.write('\0');
289                     }
290                     writer.flush();
291                 }
292                 catch (IOException JavaDoc e) {
293                     Log.debug("Error delivering raw text" + "\n" + this.toString(), e);
294                     errorDelivering = true;
295                 }
296                 finally {
297                     // Register that we finished sending data on the connection
298
SocketSendingTracker.getInstance().socketFinishedSending(socket);
299                 }
300             }
301             if (errorDelivering) {
302                 close();
303             }
304         }
305     }
306
307     /**
308      * Notifies all close listeners that the connection has been closed.
309      * Used by subclasses to properly finish closing the connection.
310      */

311     private void notifyCloseListeners() {
312         synchronized (listeners) {
313             Iterator JavaDoc itr = listeners.keySet().iterator();
314             while (itr.hasNext()) {
315                 ConnectionCloseListener listener = (ConnectionCloseListener)itr.next();
316                 listener.onConnectionClose(listeners.get(listener));
317             }
318         }
319     }
320
321     public String JavaDoc toString() {
322         return super.toString() + " socket: " + socket + " session: " + session;
323     }
324 }
Popular Tags