1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.xnap.plugin.joscar;
24
25 import java.io.IOException;
26 import java.util.Hashtable;
27 import java.util.Observable;
28 import java.util.Observer;
29
30 import org.apache.log4j.Logger;
31 import org.xnap.event.StateListener;
32 import org.xnap.event.StateSupport;
33 import org.xnap.plugin.joscar.msg.JOscarMessageQueue;
34 import org.xnap.plugin.joscar.msg.JOscarMessageSender;
35 import org.xnap.util.FiniteStateMachine;
36 import org.xnap.util.State;
37 import org.xnap.XNap;
38
39 import JOscarLib.Core.OscarConnection;
40 import JOscarLib.Integration.Event.MessagingListener;
41 import JOscarLib.Integration.Event.StatusListener;
42 import JOscarLib.Setting.Enum.StatusModeEnum;
43 import JOscarLib.Tool.OscarInterface;
44
45 public class JOscarConnection implements Observer
46 {
47
48
49
50
51 /***
52 * The connection to the oscar server.
53 */
54 private OscarConnection connection = null;
55 /***
56 * Provides listener support for state listeners.
57 */
58 private StateSupport listeners = new StateSupport(this);
59
60 private StateMachine sm = new StateMachine();
61 /***
62 * Queue for messages to be sent out. This is necessary since the joscar
63 * library doesn't use a thread itself for sending out messages, so we
64 * have to do this in order to prevent the GUI from blocking.
65 */
66 private JOscarMessageQueue queue = new JOscarMessageQueue();
67 /***
68 * Sends out the messages in the queue.
69 */
70 private JOscarMessageSender sender = new JOscarMessageSender(queue);
71
72 private static Logger logger = Logger.getLogger(JOscarConnection.class);
73
74 private static JOscarPreferences prefs = JOscarPreferences.getInstance();
75
76 private static final Hashtable TRANSITION_TABLE;
77 static {
78 State[][] table = new State[][] {
79 { State.DISCONNECTED, State.CONNECTING, },
80 { State.CONNECTING, State.CONNECTED, State.DISCONNECTED },
81 { State.CONNECTED, State.DISCONNECTING, State.DISCONNECTED },
82 { State.DISCONNECTING, State.DISCONNECTED }
83 };
84 TRANSITION_TABLE = FiniteStateMachine.createStateTable(table);
85 }
86
87
88
89 public JOscarConnection()
90 {
91
92 }
93
94
95
96 public void addMessagingListener(MessagingListener l)
97 {
98 if (connection != null) {
99 connection.addMessagingListener(l);
100 }
101 }
102
103 public void addStatusListener(StatusListener l)
104 {
105 if (connection != null) {
106 connection.addStatusListener(l);
107 }
108 }
109
110 /***
111 * Adds message to its internal message queue {@link JOscarMessageQueue}
112 * which is emptied by the {@link JOscarMessageSender}.
113 */
114 public void sendMessage(String uin, String message)
115 {
116 queue.addMessage(uin, message);
117 }
118
119 /***
120 * Adds SMS message to its internal message queue {@link
121 * JOscarMessageQueue} which is emptied by the {@link
122 * JOscarMessageSender}.
123 */
124 public void sendSMS(String uin, String message)
125 {
126 queue.addSMS(uin, message);
127 }
128
129 public void addUser(String uin)
130 {
131 queue.addUser(uin);
132 }
133
134 public void connect()
135 {
136 setState(State.CONNECTING);
137 }
138
139 public void disconnect()
140 {
141 setState(State.DISCONNECTING);
142 }
143
144 public State getState()
145 {
146 return sm.getState();
147 }
148
149 private void setState(State newState)
150 {
151 sm.setState(newState);
152 listeners.fireStateChanged();
153 }
154
155 private void setState(State newState, String message)
156 {
157 sm.setState(newState, message);
158 listeners.fireStateChanged();
159 }
160
161 public void addStateListener(StateListener listener)
162 {
163 listeners.addStateListener(listener);
164 }
165
166 public void removeStateListener(StateListener listener)
167 {
168 listeners.removeStateListener(listener);
169 }
170
171 /***
172 * Implements the {@link Observer} interface.
173 *
174 * The <code>connection</code> notifies us when it's connected.
175 */
176 public void update(Observable obs, Object obj)
177 {
178 if (connection.isLogged()) {
179 OscarInterface.changeStatus
180 (connection,new StatusModeEnum(StatusModeEnum.ONLINE));
181 setState(State.CONNECTED);
182 }
183 else {
184
185 setState(State.DISCONNECTED);
186 }
187 }
188
189
190 private class StateMachine extends FiniteStateMachine
191 {
192 public StateMachine()
193 {
194 super(State.DISCONNECTED, TRANSITION_TABLE);
195 }
196
197 protected synchronized void stateChanged(State oldState,
198 State newState)
199 {
200 if (newState == State.CONNECTING) {
201 createConnection();
202 }
203 else if (newState == State.CONNECTED) {
204 sender.setConnection(connection);
205 sender.start();
206 }
207 else if (newState == State.DISCONNECTING) {
208 closeConnection();
209 }
210 else if (newState == State.DISCONNECTED) {
211 sender.stop();
212 queue.clear();
213 }
214 }
215
216 /***
217 * Connect to the oscar server.
218 */
219 private void createConnection()
220 {
221 connection = new OscarConnection(prefs.getServerHost(),
222 prefs.getServerPort(),
223 prefs.getUsername(),
224 prefs.getPassword());
225 if (XNap.isRunFromCvs()) {
226 connection.getPacketAnalyser().setDebug(true);
227 }
228 connection.addObserver(JOscarConnection.this);
229 }
230
231 /***
232 * Closes the connection.
233 */
234 private void closeConnection()
235 {
236 try {
237 connection.getClient().disconnect();
238 }
239 catch (IOException ie) {
240 logger.debug("Error stopping connection", ie);
241 }
242 connection = null;
243 JOscarConnection.this.setState(State.DISCONNECTED);
244 }
245 }
246
247 }