1 /*------------------------------------------------------------------------------
2 Name: TestCallback.java
3 Project: xmlBlaster.org
4 Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
5 Comment: Login/logout test for xmlBlaster
6 Version: $Id: TestCallback.java 16172 2007-05-22 12:42:47Z ruff $
7 ------------------------------------------------------------------------------*/
8 package org.xmlBlaster.test.qos;
9
10 import java.util.logging.Logger;
11
12 import junit.framework.Test;
13 import junit.framework.TestCase;
14 import junit.framework.TestSuite;
15
16 import org.xmlBlaster.client.I_Callback;
17 import org.xmlBlaster.client.I_XmlBlasterAccess;
18 import org.xmlBlaster.client.key.UpdateKey;
19 import org.xmlBlaster.client.qos.ConnectQos;
20 import org.xmlBlaster.client.qos.EraseReturnQos;
21 import org.xmlBlaster.client.qos.UpdateQos;
22 import org.xmlBlaster.test.Util;
23 import org.xmlBlaster.util.Global;
24 import org.xmlBlaster.util.MsgUnit;
25 import org.xmlBlaster.util.XmlBlasterException;
26
27
28 /**
29 * This client test dead letter generation on callback problems.
30 * <p />
31 * This client may be invoked multiple time on the same xmlBlaster server,
32 * as it cleans up everything after his tests are done.
33 */
34 public class TestCallback extends TestCase implements I_Callback
35 {
36 private static String ME = "TestCallback";
37 private final Global glob;
38 private static Logger log = Logger.getLogger(TestCallback.class.getName());
39 private String name;
40 private String passwd = "secret";
41 private int numReceived = 0; // error checking
42
43 private boolean isDeadMessage = false;
44 private String subscribeDeadMessageOid = null;
45 private I_XmlBlasterAccess conAdmin = null;
46 private String publishOid = null;
47
48 private boolean isSocket = false;
49
50 /**
51 * Constructs the TestCallback object.
52 * <p />
53 * @param testName The name used in the test suite
54 * @param name The name to login to the xmlBlaster
55 */
56 public TestCallback(Global glob, String testName, String name)
57 {
58 super(testName);
59 this.glob = glob;
60
61 this.name = name;
62 }
63
64 /**
65 * Sets up the fixture.
66 * <p />
67 * Connect to xmlBlaster and login as admin, subscribe to dead letters
68 */
69 protected void setUp()
70 {
71 String driverType = glob.getProperty().get("client.protocol", "dummy");
72 if (driverType.equalsIgnoreCase("SOCKET"))
73 isSocket = true;
74 else {
75 driverType = glob.getProperty().get("protocol", "dummy");
76 if (driverType.equalsIgnoreCase("SOCKET"))
77 isSocket = true;
78 }
79
80 if (isSocket) {
81 log.warning("callback test ignored for driverType=" + driverType + " as callback server uses same socket as invoke channel");
82 return;
83 }
84
85 try {
86 Global globAdmin = this.glob.getClone(null);
87 conAdmin = globAdmin.getXmlBlasterAccess();
88 ConnectQos qos = new ConnectQos(globAdmin, "admin", passwd);
89 conAdmin.connect(qos, this);
90
91 subscribeDeadMessageOid = conAdmin.subscribe("<key oid='__sys__deadMessage'/>", null).getSubscriptionId();
92 log.info("Success: Subscribe on " + subscribeDeadMessageOid + " done");
93 }
94 catch (Exception e) {
95 log.severe(e.toString());
96 assertTrue(e.toString(), false);
97 }
98 }
99
100 /**
101 * Tears down the fixture.
102 * <p />
103 * cleaning up .... erase() the previous message OID and logout
104 */
105 protected void tearDown()
106 {
107 if (isSocket) return;
108 try {
109 if (conAdmin != null) {
110 EraseReturnQos[] strArr = conAdmin.erase("<key oid='" + publishOid + "'/>", null);
111 if (strArr.length != 1) log.severe("ERROR: Erased " + strArr.length + " messages");
112 conAdmin.disconnect(null);
113 }
114 }
115 catch (Exception e) {
116 log.severe(e.toString());
117 e.printStackTrace();
118 assertTrue(e.toString(), false);
119 }
120 }
121
122 /**
123 * We expect dead letters after destroying our callback server.
124 */
125 public void testCallbackFailure()
126 {
127 if (isSocket) return;
128 log.info("testCallbackFailure() ...");
129 try {
130 log.info("Connecting ...");
131 Global globSub = this.glob.getClone(null);
132 I_XmlBlasterAccess con = globSub.getXmlBlasterAccess();
133 ConnectQos qos = new ConnectQos(globSub, name, passwd);
134 con.connect(qos, this); // Login to xmlBlaster
135
136 try {
137 con.getCbServer().shutdown(); // Destroy the callback server
138 }
139 catch (Throwable e) {
140 log.severe("testCallbackFailure: " + e.toString());
141 fail(e.toString());
142 }
143
144 String subscribeOid = con.subscribe("<key oid='testCallbackMsg'/>", null).getSubscriptionId();
145 log.info("Success: Subscribe on " + subscribeOid + " done");
146
147 MsgUnit msgUnit = new MsgUnit("<key oid='testCallbackMsg'/>", "Bla".getBytes(), null);
148 publishOid = con.publish(msgUnit).getKeyOid();
149 log.info("Success: Publishing done, returned oid=" + publishOid);
150
151 waitOnUpdate(2000L, 1);
152 assertTrue("Expected a dead letter", isDeadMessage);
153 isDeadMessage = false;
154
155 //Global.waitOnKeyboardHit("Session destroyed? Hit a key to continue");
156
157 // Check with user "admin" if session of "Tim" has disappeared
158 try {
159 MsgUnit[] msgs = Util.adminGet(glob, "__cmd:?clientList");
160 assertEquals("Can't access __cmd:?clientList", 1, msgs.length);
161 log.info("Got clientList=" + msgs[0].getContentStr());
162 assertEquals("Session of " + name + " was not destroyed by failing callback",
163 -1, msgs[0].getContentStr().indexOf(name));
164 }
165 catch (XmlBlasterException e) {
166 fail("Session was not destroyed: " + e.toString());
167 }
168 }
169 catch (Exception e) {
170 log.severe(e.toString());
171 assertTrue(e.toString(), false);
172 }
173 log.info("Success in testCallbackFailure()");
174 }
175
176 /**
177 * This is the callback method invoked from xmlBlaster
178 * delivering us a new asynchronous message.
179 * @see org.xmlBlaster.client.I_Callback#update(String, UpdateKey, byte[], UpdateQos)
180 */
181 public String update(String cbSessionId, UpdateKey updateKey, byte[] content, UpdateQos updateQos)
182 {
183 log.info("Receiving update of a message " + updateKey.getOid());
184 numReceived++;
185 isDeadMessage = updateKey.isDeadMessage();
186 return "";
187 }
188
189 /**
190 * Little helper, waits until the wanted number of messages are arrived
191 * or returns when the given timeout occurs.
192 * <p />
193 * @param timeout in milliseconds
194 * @param numWait how many messages to wait
195 */
196 private void waitOnUpdate(final long timeout, final int numWait)
197 {
198 long pollingInterval = 50L; // check every 0.05 seconds
199 if (timeout < 50) pollingInterval = timeout / 10L;
200 long sum = 0L;
201 // check if too few are arriving
202 while (numReceived < numWait) {
203 try { Thread.sleep(pollingInterval); } catch( InterruptedException i) {}
204 sum += pollingInterval;
205 assertTrue("Timeout of " + timeout + " occurred without update", sum <= timeout);
206 }
207
208 // check if too many are arriving
209 try { Thread.sleep(timeout); } catch( InterruptedException i) {}
210 assertEquals("Wrong number of messages arrived", numWait, numReceived);
211
212 numReceived = 0;
213 }
214
215 /**
216 * Method is used by TestRunner to load these tests
217 */
218 public static Test suite()
219 {
220 TestSuite suite= new TestSuite();
221 String loginName = "Tim";
222 suite.addTest(new TestCallback(new Global(), "testCallbackFailure", "Tim"));
223 return suite;
224 }
225
226 /**
227 * Invoke:
228 * <pre>
229 * java -Djava.compiler= junit.textui.TestRunner org.xmlBlaster.test.qos.TestCallback
230 *
231 * java org.xmlBlaster.test.qos.TestCallback -dispatch/callback/retries 0 -dispatch/callback/delay 3000
232 * </pre>
233 */
234 public static void main(String args[])
235 {
236 TestCallback testSub = new TestCallback(new Global(args), "TestCallback", "Tim");
237 testSub.setUp();
238 testSub.testCallbackFailure();
239 testSub.tearDown();
240 }
241 }
syntax highlighted by Code2HTML, v. 0.9.1