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