1 /*------------------------------------------------------------------------------
2 Name: TestLoginLogoutEvent.java
3 Project: xmlBlaster.org
4 Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
5 ------------------------------------------------------------------------------*/
6 package org.xmlBlaster.test.authentication;
7
8 import java.util.StringTokenizer;
9 import java.util.logging.Logger;
10 import java.util.logging.Level;
11 import org.xmlBlaster.util.Global;
12 import org.xmlBlaster.client.qos.ConnectQos;
13 import org.xmlBlaster.client.I_XmlBlasterAccess;
14 import org.xmlBlaster.util.XmlBlasterException;
15 import org.xmlBlaster.util.MsgUnit;
16 import org.xmlBlaster.util.SessionName;
17 import org.xmlBlaster.util.def.Constants;
18
19 import org.xmlBlaster.test.MsgInterceptor;
20
21 import junit.framework.*;
22
23
24 /**
25 * This client tests for login/logout events of other clients.
26 * <p />
27 * IMPORTANT NOTE: Switch on the event plugins in xmlBlasterPlugins.xml for this test!
28 * <p />
29 * There are two internal messages which hold login and logout events.
30 * You can subscribe to "__sys__Login" to be notified when a new client logs in,
31 * and to "__sys__Logout" to be notified when a client logs out.
32 * The message content contains the unique login name of this client.
33 * <p />
34 * Tests the '__cmd:?clientList' feature as well.
35 * <p />
36 * This client may be invoked multiple time on the same xmlBlaster server,
37 * as it cleans up everything after his tests are done.
38 * <p />
39 * Invoke examples:<br />
40 * <pre>
41 * java junit.textui.TestRunner org.xmlBlaster.test.authentication.TestLoginLogoutEvent
42 * java junit.swingui.TestRunner -noloading org.xmlBlaster.test.authentication.TestLoginLogoutEvent
43 * </pre>
44 */
45 public class TestLoginLogoutEvent extends TestCase
46 {
47 private Global glob;
48 private static Logger log = Logger.getLogger(TestLoginLogoutEvent.class.getName());
49
50 private I_XmlBlasterAccess firstConnection;
51 private String firstName;
52
53 private I_XmlBlasterAccess secondConnection;
54 private String secondName;
55
56 private String expectedName;
57
58 private String passwd = "secret";
59
60 private MsgInterceptor updateInterceptFirst; // collects updated messages
61 private MsgInterceptor updateInterceptSecond; // collects updated messages
62
63 /**
64 * Constructs the TestLoginLogoutEvent object.
65 * <p />
66 * @param testName The name used in the test suite
67 * @param firstName The name to login to the xmlBlaster
68 * @param secondName The name to login to the xmlBlaster again
69 */
70 public TestLoginLogoutEvent(Global glob, String testName, String firstName, String secondName)
71 {
72 super(testName);
73 this.glob = glob;
74
75 this.firstName = firstName;
76 this.secondName = secondName;
77 }
78
79
80 /**
81 * Sets up the fixture.
82 * <p />
83 * Connect to xmlBlaster and login
84 */
85 protected void setUp()
86 {
87 try {
88 Global firstGlob = glob.getClone(null);
89 firstConnection = firstGlob.getXmlBlasterAccess(); // Find orb
90 ConnectQos qos = new ConnectQos(firstGlob, firstName, passwd);
91 this.updateInterceptFirst = new MsgInterceptor(firstGlob, log, null);
92 firstConnection.connect(qos, this.updateInterceptFirst); // Login to xmlBlaster
93 }
94 catch (Exception e) {
95 log.severe(e.toString());
96 e.printStackTrace();
97 }
98
99 this.updateInterceptFirst.clear();
100 }
101
102
103 /**
104 * Tears down the fixture.
105 * <p />
106 * cleaning up .... erase() the previous message OID and logout
107 */
108 protected void tearDown()
109 {
110 String xmlKey = "<key oid='__sys__Logout' queryType='EXACT'></key>";
111 String qos = "<qos></qos>";
112 if (this.firstConnection != null) {
113 try {
114 this.firstConnection.unSubscribe(xmlKey, qos);
115 } catch(XmlBlasterException e) {
116 log.warning("XmlBlasterException: " + e.getMessage());
117 assertTrue("unSubscribe - XmlBlasterException: " + e.getMessage(), false);
118 }
119
120 this.firstConnection.disconnect(null);
121 this.firstConnection = null;
122 }
123
124 if (this.secondConnection != null) {
125 this.secondConnection.disconnect(null);
126 this.secondConnection = null;
127 }
128 this.glob = null;
129
130 Global.instance().shutdown();
131 }
132
133
134 /**
135 * Subscribe to login events with oid="__sys__Login" or "__sys__Logout"
136 */
137 public void subscribe(String oid)
138 {
139 if (log.isLoggable(Level.FINE)) log.fine("Subscribing to login events ...");
140 String xmlKey = "<key oid='" + oid + "' queryType='EXACT'></key>";
141 String qos = "<qos></qos>";
142 try {
143 String subscribeOid = firstConnection.subscribe(xmlKey, qos).getSubscriptionId();
144 assertTrue("returned null subscribeOid", subscribeOid != null);
145 log.info("Success: Subscribe on " + subscribeOid + " done");
146 } catch(XmlBlasterException e) {
147 log.warning("XmlBlasterException: " + e.getMessage());
148 assertTrue("subscribe - XmlBlasterException: " + e.getMessage(), false);
149 }
150 }
151
152
153 /**
154 * TEST: Test to receive a login and a logout event.
155 */
156 public void testLoginLogout()
157 {
158 long sleep = 1000L;
159
160 expectedName = firstName; // my first login name should be returned on this subscribe
161 subscribe("__sys__Login");
162 /* NO not anymore, it is now volatile
163 // expecting a login event message (the login event message exists from my own login)
164 assertEquals("Missing my login event", 1, this.updateInterceptFirst.waitOnUpdate(sleep, "__sys__Login", Constants.STATE_OK));
165 {
166 String content = this.updateInterceptFirst.getMsgs()[0].getContentStr();
167 log.info("Received login event for " + content);
168 assertEquals("Wrong login name", expectedName, content);
169 this.updateInterceptFirst.clear();
170 }
171 */
172
173 expectedName = null; // no check (the logout event exists with AllTests but not when this test is run alone
174 subscribe("__sys__Logout");
175 try { Thread.sleep(1000L); } catch( InterruptedException i) {} // no check
176 this.updateInterceptFirst.clear();
177
178 expectedName = secondName; // second name should be returned on this login
179 try {
180 Global secondGlob = glob.getClone(null);
181 this.secondConnection = secondGlob.getXmlBlasterAccess(); // Find orb
182 ConnectQos qos = new ConnectQos(secondGlob, secondName, passwd); // == "<qos></qos>";
183 this.updateInterceptSecond = new MsgInterceptor(secondGlob, log, null);
184 this.secondConnection.connect(qos, this.updateInterceptSecond); // Login to xmlBlaster
185
186 // login event arrived?
187 assertEquals("Missing my login event", 1, this.updateInterceptFirst.waitOnUpdate(sleep, "__sys__Login", Constants.STATE_OK));
188 String eventType = this.updateInterceptFirst.getMsgs()[0].getContentStr();
189 String subjectId = this.updateInterceptFirst.getMsgs()[0].getUpdateQos().getClientProperty("_subjectId", "");
190 log.info("Received login event '" + eventType + "' for " + subjectId);
191 assertEquals("Wrong login name", expectedName, subjectId);
192 this.updateInterceptFirst.clear();
193
194 assertEquals("Not expected update for second con", 0, this.updateInterceptSecond.waitOnUpdate(500L));
195 this.updateInterceptSecond.clear();
196
197 // Test the '__cmd:?clientList' feature:
198 MsgUnit[] msgArr = secondConnection.get(
199 "<key oid='__cmd:?clientList'/>",
200 "<qos/>");
201 assertTrue("Expected on __cmd:?clientList", msgArr.length == 1);
202 String clients = new String(msgArr[0].getContent());
203 log.info("Current '__cmd:?clientList' is\n" + clients);
204 StringTokenizer st = new StringTokenizer(clients, ","); // joe,jack,averell,...
205 int found = 0;
206 while (st.hasMoreTokens()) {
207 String client = (String)st.nextToken();
208 log.info("Parsing name=" + client);
209 SessionName sessionName = new SessionName(glob, client);
210 if (sessionName.getLoginName().equals(this.firstName))
211 found++;
212 else if (sessionName.getLoginName().equals(this.secondName))
213 found++;
214 }
215 assertTrue("Check of '__cmd:?clientList' failed", found==2);
216 }
217 catch (XmlBlasterException e) {
218 log.severe(e.getMessage());
219 assertTrue("Second login failed", false);
220 }
221
222
223 expectedName = secondName; // second name should be returned on this login
224 secondConnection.disconnect(null);
225 secondConnection = null;
226
227 // expecting a logout event message
228 {
229 assertEquals("Missing my logout event", 1, this.updateInterceptFirst.waitOnUpdate(sleep, "__sys__Logout", Constants.STATE_OK));
230 //String content = this.updateInterceptFirst.getMsgs()[0].getContentStr();
231 String subjectId = this.updateInterceptFirst.getMsgs()[0].getUpdateQos().getClientProperty("_subjectId", "");
232 log.info("Received logout event for " + subjectId);
233 assertEquals("Wrong logout name", expectedName, subjectId);
234 this.updateInterceptFirst.clear();
235 }
236
237 assertEquals("Not expected update for second con", 0, this.updateInterceptSecond.waitOnUpdate(500L));
238 this.updateInterceptSecond.clear();
239 }
240
241 /** ----> see this.updateInterceptFirst
242 * This is the callback method invoked from xmlBlaster
243 * delivering us a new asynchronous message.
244 * @see org.xmlBlaster.client.I_Callback#update(String, UpdateKey, byte[], UpdateQos)
245 public String update(String cbSessionId, UpdateKey updateKey, byte[] content, UpdateQos updateQos)
246 {
247 String name = new String(content);
248 if (name.startsWith("_"))
249 return ""; // Ignore internal logins from plugins
250 numReceived++;
251 log.info(cbSessionId + " - Receiving update of a message " + updateKey.getOid() + ", event for client " + name);
252
253 if (expectedName != null)
254 assertEquals("Wrong login name returned", expectedName, name);
255 return "";
256 }
257 */
258
259 /**
260 * Little helper, waits until the wanted number of messages are arrived
261 * or returns when the given timeout occurs.
262 * <p />
263 * @param timeout in milliseconds
264 * @param numWait how many messages to wait
265 private void waitOnUpdate(final long timeout, final int numWait)
266 {
267 long pollingInterval = 50L; // check every 0.05 seconds
268 if (timeout < 50) pollingInterval = timeout / 10L;
269 long sum = 0L;
270 // check if too few are arriving
271 while (numReceived < numWait) {
272 try { Thread.sleep(pollingInterval); } catch( InterruptedException i) {}
273 sum += pollingInterval;
274 assertTrue("Timeout of " + timeout + " occurred without update", sum <= timeout);
275 }
276
277 // check if too many are arriving
278 try { Thread.sleep(timeout); } catch( InterruptedException i) {}
279 assertEquals("Wrong number of messages arrived", numWait, numReceived);
280
281 numReceived = 0;
282 }
283
284 */
285
286 /**
287 * Method is used by TestRunner to load these tests
288 */
289 public static Test suite()
290 {
291 TestSuite suite= new TestSuite();
292 suite.addTest(new TestLoginLogoutEvent(new Global(), "testLoginLogout", "Gesa", "Ben"));
293 return suite;
294 }
295
296
297 /**
298 * Invoke: java org.xmlBlaster.test.authentication.TestLoginLogoutEvent
299 */
300 public static void main(String args[])
301 {
302 TestLoginLogoutEvent testSub = new TestLoginLogoutEvent(new Global(args), "TestLoginLogoutEvent", "Tim", "Joe");
303 testSub.setUp();
304 testSub.testLoginLogout();
305 testSub.tearDown();
306 }
307 }
syntax highlighted by Code2HTML, v. 0.9.1