1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package net.sf.bluecove;
26
27 import java.io.IOException;
28 import java.util.Enumeration;
29 import java.util.Hashtable;
30 import java.util.Vector;
31
32 import javax.bluetooth.BluetoothStateException;
33 import javax.bluetooth.DiscoveryAgent;
34 import javax.bluetooth.LocalDevice;
35 import javax.bluetooth.UUID;
36 import javax.microedition.io.Connection;
37 import javax.microedition.io.Connector;
38
39 import org.bluecove.tester.log.Logger;
40 import org.bluecove.tester.util.IOUtils;
41 import org.bluecove.tester.util.RuntimeDetect;
42 import org.bluecove.tester.util.StringUtils;
43 import org.bluecove.tester.util.TimeUtils;
44
45 import net.sf.bluecove.se.JavaSECommon;
46 import net.sf.bluecove.util.BluetoothTypesInfo;
47 import net.sf.bluecove.util.CollectionUtils;
48 import net.sf.bluecove.util.CountStatistic;
49 import net.sf.bluecove.util.IntVar;
50 import net.sf.bluecove.util.TimeStatistic;
51
52
53
54
55 public class TestResponderClient extends TestResponderCommon implements Runnable, CanShutdown {
56
57 public static int countSuccess = 0;
58
59 public static FailureLog failure = new FailureLog("Client failure");
60
61 public static int discoveryCount = 0;
62
63 public static int connectionCount = 0;
64
65 public static int discoveryDryCount = 0;
66
67 public static int discoverySuccessCount = 0;
68
69 public static long lastSuccessfulDiscovery;
70
71 public static int countConnectionThreads = 0;
72
73 private int connectedConnectionsExpect = 1;
74
75 private int connectionLogPrefixLength = 0;
76
77 private int connectedConnectionsInfo = 1;
78
79 private Vector concurrentConnections = new Vector();
80
81 public static CountStatistic concurrentStatistic = new CountStatistic();
82
83 public static TimeStatistic connectionDuration = new TimeStatistic();
84
85 public static CountStatistic connectionRetyStatistic = new CountStatistic();
86
87 public Thread thread;
88
89 private Object threadLocalBluetoothStack;
90
91 boolean stoped = false;
92
93 TestClientConfig config;
94
95 boolean isRunning = false;
96
97 boolean runStressTest = false;
98
99 TestClientBluetoothInquirer bluetoothInquirer;
100
101 public static Hashtable recentDeviceNames = new Hashtable
102
103 boolean configured = false;
104
105 static int sdAttrRetrievableMax = 255;
106
107 public static synchronized void clear() {
108 countSuccess = 0;
109 failure.clear();
110 discoveryCount = 0;
111 concurrentStatistic.clear();
112 connectionDuration.clear();
113 }
114
115 public TestResponderClient() throws BluetoothStateException {
116 this(true);
117 }
118
119 public TestResponderClient(boolean logLocalDevice) throws BluetoothStateException {
120
121 config = new TestClientConfig();
122
123 if (logLocalDevice) {
124 TestResponderCommon.startLocalDevice();
125 }
126 threadLocalBluetoothStack = Configuration.threadLocalBluetoothStack;
127
128 String v = LocalDevice.getProperty("bluetooth.sd.attr.retrievable.max");
129 if (v != null) {
130 sdAttrRetrievableMax = Integer.valueOf(v).intValue();
131 if ((sdAttrRetrievableMax > 7) && (RuntimeDetect.isJ2ME)) {
132 sdAttrRetrievableMax = 7;
133 }
134 }
135
136 }
137
138 static boolean isMultiProtocol() {
139 return ((Configuration.supportL2CAP) && Configuration.testL2CAP.booleanValue() && Configuration.testRFCOMM
140 .booleanValue());
141 }
142
143 public void connectAndTest(String serverURL, String urlArgs, IntVar firstCase, IntVar lastCase,
144 TestResponderClientConnection connectionHandler) {
145 String deviceAddress = BluetoothTypesInfo.extractBluetoothAddress(serverURL);
146 String deviceName = niceDeviceName(deviceAddress);
147 long start = System.currentTimeMillis();
148 Logger.debug("connect:" + deviceName + " " + serverURL);
149 String logPrefix = "";
150 if (isMultiProtocol()) {
151 deviceName = connectionHandler.protocolID() + " " + deviceName;
152 }
153 if (connectedConnectionsExpect > 1) {
154 logPrefix = "[" + StringUtils.padRight(deviceName, connectionLogPrefixLength, ' ') + "] ";
155 }
156 for (int testType = firstCase.getValue(); (!stoped) && (runStressTest || testType <= lastCase.getValue()); testType++) {
157 Connection conn = null;
158 ConnectionHolder c = null;
159 TestStatus testStatus = new TestStatus();
160 testStatus.pairBTAddress = deviceAddress;
161 TestTimeOutMonitor monitor = null;
162 long connectionStartTime = 0;
163 try {
164 if (!runStressTest) {
165 Logger.debug(logPrefix + "test #" + testType + " connects");
166 } else {
167 testType = Configuration.STERSS_TEST_CASE.getValue();
168 }
169 int connectionOpenTry = 0;
170 while ((conn == null) && (!stoped)) {
171 try {
172 conn = Connector.open(serverURL + urlArgs, Connector.READ_WRITE, true);
173 } catch (IOException e) {
174 connectionOpenTry++;
175 if ((stoped) || (connectionOpenTry > CommunicationTester.clientConnectionOpenRetry)) {
176 throw e;
177 }
178 Logger.debug(logPrefix + "Connector error", e);
179 Thread.sleep(Configuration.clientSleepOnConnectionRetry);
180 Logger.debug(logPrefix + "connect retry:" + connectionOpenTry);
181 String cCount = LocalDevice.getProperty("bluecove.connections");
182 if ((cCount != null) && (!"0".equals(cCount))) {
183 Logger.debug(logPrefix + "has connections:" + cCount);
184 }
185 }
186 }
187 if (stoped) {
188 return;
189 }
190 c = connectionHandler.connected(conn);
191
192 connectionStartTime = System.currentTimeMillis();
193 connectionRetyStatistic.add(connectionOpenTry);
194 connectionCount++;
195
196 c.registerConcurrent(concurrentConnections);
197 c.concurrentNotify();
198
199 if (connectedConnectionsInfo < c.concurrentCount) {
200 connectedConnectionsInfo = c.concurrentCount;
201 Logger.info(logPrefix + "now connected:" + connectedConnectionsInfo);
202 synchronized (TestResponderClient.this) {
203 TestResponderClient.this.notifyAll();
204 }
205 }
206 c.active();
207 monitor = TestTimeOutMonitor.create(logPrefix + "test #" + testType, c,
208 Configuration.clientTestTimeOutSec);
209 if (!runStressTest) {
210 Logger.debug(logPrefix + "run test #" + testType);
211 } else {
212 Logger.debug(logPrefix + "connected:" + connectionCount);
213 if (connectionCount % 5 == 0) {
214 Logger.debug("Test time " + TimeUtils.secSince(start));
215 }
216 }
217 if (testType > Consts.TEST_SERVER_TERMINATE) {
218 Configuration.setLastServerURL(serverURL);
219 }
220
221 connectionHandler.executeTest(testType, testStatus);
222
223 if (monitor.isShutdownCalled()) {
224 failure.addFailure(deviceName + " test #" + testType + " " + testStatus.getName()
225 + " terminated by by TimeOut");
226 } else if (testStatus.isError) {
227 failure.addFailure(deviceName + " test #" + testType + " " + testStatus.getName());
228 } else if (testStatus.isSuccess) {
229 countSuccess++;
230 Logger.debug(logPrefix + "test #" + testType + " " + testStatus.getName() + ": OK");
231 } else if (testStatus.streamClosed) {
232 Logger.debug(logPrefix + "see server log");
233 } else {
234 connectionHandler.replySuccess(logPrefix, testType, testStatus);
235 countSuccess++;
236 Logger.debug(logPrefix + "test #" + testType + " " + testStatus.getName() + ": OK");
237 }
238 if (connectionCount % 5 == 0) {
239 Logger.info("*Success:" + countSuccess + " Failure:" + failure.countFailure);
240 }
241 Configuration.setLastServerURL(serverURL);
242
243
244 if ((connectedConnectionsExpect > 1) && (connectedConnectionsInfo < connectedConnectionsExpect)) {
245 synchronized (TestResponderClient.this) {
246 try {
247 TestResponderClient.this.wait(3 * 1000);
248 } catch (InterruptedException e) {
249 break;
250 }
251 }
252 Logger.debug(logPrefix + "concurrentCount " + c.concurrentCount);
253 }
254 } catch (Throwable e) {
255 if (!stoped) {
256 if ((monitor != null) && (monitor.isShutdownCalled())) {
257 failure.addFailure(deviceName + " test #" + testType + " " + testStatus.getName()
258 + " terminated by by TimeOut");
259 } else if (testType < Consts.TEST_SERVER_TERMINATE) {
260 failure.addFailure(deviceName + " test #" + testType + " " + testStatus.getName(), e);
261 }
262 }
263 Logger.error(deviceName + " test #" + testType + " " + testStatus.getName(), e);
264 } finally {
265 if (connectionStartTime != 0) {
266 connectionDuration.add(TimeUtils.since(connectionStartTime));
267 }
268 if (monitor != null) {
269 monitor.finish();
270 }
271 if (c != null) {
272 c.disconnected();
273 if (c.concurrentCount != 0) {
274 concurrentStatistic.add(c.concurrentCount);
275 }
276 c.shutdown();
277 } else {
278 IOUtils.closeQuietly(conn);
279 }
280 }
281 if ((Configuration.clientTestStopOnErrorCount != 0)
282 && (failure.countFailure >= Configuration.clientTestStopOnErrorCount)) {
283 shutdown();
284 }
285
286 if (!stoped) {
287 try {
288 Thread.sleep(Configuration.clientSleepBetweenConnections);
289 } catch (InterruptedException e) {
290 break;
291 }
292 }
293 }
294 if (!Configuration.clientContinuous.booleanValue()) {
295 connectionHandler.sendStopServerCmd(serverURL);
296 }
297 }
298
299 private boolean connectAndTest(String serverURL) {
300 if (BluetoothTypesInfo.isRFCOMM(serverURL)) {
301 if (Configuration.testRFCOMM.booleanValue()) {
302 TestResponderClientRFCOMM.connectAndTest(this, serverURL);
303 return true;
304 }
305 } else if (BluetoothTypesInfo.isL2CAP(serverURL)) {
306 if (Configuration.supportL2CAP) {
307 if (Configuration.testL2CAP.booleanValue()) {
308 TestResponderClientL2CAP.connectAndTest(this, serverURL);
309 return true;
310 }
311 } else {
312 Logger.warn("Can't test L2CAP on this stack");
313 }
314 } else {
315 Logger.warn("No tests for connection type " + serverURL);
316 }
317 return false;
318 }
319
320 private class ClientConnectionTread extends Thread {
321
322 String url;
323
324 boolean started;
325
326 ClientConnectionTread(String url) {
327
328
329 ++countConnectionThreads;
330
331 this.url = url;
332 }
333
334 public void run() {
335 RuntimeDetect.cldcStub.setThreadLocalBluetoothStack(threadLocalBluetoothStack);
336 started = connectAndTest(url);
337 }
338 }
339
340 private void connectAndTest(Vector urls) {
341 int numberOfURLs = urls.size();
342 if ((!Configuration.clientTestConnectionsMultipleThreads) || (numberOfURLs == 1)) {
343 connectedConnectionsExpect = 1;
344 connectedConnectionsInfo = 1;
345 for (Enumeration en = urls.elements(); en.hasMoreElements();) {
346 if (stoped) {
347 break;
348 }
349 String url = (String) en.nextElement();
350 connectAndTest(url);
351 }
352 } else {
353 connectedConnectionsExpect = numberOfURLs;
354 connectedConnectionsInfo = 1;
355
356 connectionLogPrefixLength = 0;
357 for (Enumeration en = urls.elements(); en.hasMoreElements();) {
358 String deviceAddress = BluetoothTypesInfo.extractBluetoothAddress((String) en.nextElement());
359 String deviceName = niceDeviceName(deviceAddress);
360 if (deviceName.length() > connectionLogPrefixLength) {
361 connectionLogPrefixLength = deviceName.length();
362 }
363 }
364
365 if (isMultiProtocol()) {
366 connectionLogPrefixLength += 3;
367 }
368
369 Logger.debug("start " + numberOfURLs + " threads");
370 Vector threads = new Vector();
371 for (Enumeration en = urls.elements(); en.hasMoreElements();) {
372 ClientConnectionTread t = new ClientConnectionTread((String) en.nextElement());
373 t.start();
374 threads.addElement(t);
375 }
376 int connectedConnectionsStartedExpect = 0;
377 for (Enumeration en = threads.elements(); en.hasMoreElements();) {
378 ClientConnectionTread t = (ClientConnectionTread) en.nextElement();
379 if (t.started) {
380 connectedConnectionsStartedExpect++;
381 }
382 try {
383 t.join();
384 } catch (InterruptedException e) {
385 break;
386 }
387 }
388 if (connectedConnectionsInfo < connectedConnectionsStartedExpect) {
389 if (!stoped) {
390 failure.addFailure("Fails to establish " + connectedConnectionsStartedExpect
391 + " connections same time");
392 Logger.error("Fails to establish " + connectedConnectionsStartedExpect + " connections same time");
393 }
394 } else {
395 if (connectedConnectionsInfo > 1) {
396 Logger.info("Established " + connectedConnectionsInfo + " connections same time");
397 }
398 }
399 }
400 }
401
402 public void configured() {
403 synchronized (this) {
404 configured = true;
405 this.notifyAll();
406 }
407 }
408
409 public long lastActivityTime() {
410 return lastSuccessfulDiscovery;
411 }
412
413 public void run() {
414 synchronized (this) {
415 while (!configured) {
416 try {
417 this.wait();
418 } catch (InterruptedException e) {
419 return;
420 }
421 }
422 }
423 RuntimeDetect.cldcStub.setThreadLocalBluetoothStack(threadLocalBluetoothStack);
424
425 Logger.debug(config.logID + "Client started..." + TimeUtils.timeNowToString());
426 isRunning = true;
427 try {
428 bluetoothInquirer = new TestClientBluetoothInquirer(config);
429 Switcher.clientStarted(this);
430
431 int startTry = 0;
432 if (config.connectURL != null) {
433 if (!config.connectURL.equals("")) {
434 bluetoothInquirer.serverURLs.addElement(config.connectURL);
435 }
436 } else if (config.connectDevice != null) {
437 Configuration.clientContinuousDiscoveryDevices = false;
438 bluetoothInquirer.devices.addElement(new RemoteDeviceIheritance(config.connectDevice));
439 }
440
441 while (!stoped) {
442 if ((config.connectURL != null) && (config.connectURL.equals(""))) {
443 try {
444 bluetoothInquirer.serverURLs.removeAllElements();
445 DiscoveryAgent discoveryAgent = LocalDevice.getLocalDevice().getDiscoveryAgent();
446 UUID uuid = (config.searchOnlyBluecoveUuid) ? Configuration.blueCoveUUID()
447 : Configuration.discoveryUUID;
448 String url = discoveryAgent.selectService(uuid, Configuration.getRequiredSecurity(), false);
449 if (url != null) {
450 Logger.debug(config.logID + "selectService service found " + url);
451 bluetoothInquirer.serverURLs.addElement(url);
452 } else {
453 Logger.debug(config.logID + "selectService service not found");
454 }
455 } catch (BluetoothStateException e) {
456 Logger.error(config.logID + "Cannot selectService", e);
457 }
458 } else if ((!bluetoothInquirer.hasServers())
459 || (Configuration.clientContinuousDiscovery.booleanValue() && (config.connectURL == null))
460 || (!Configuration.clientTestConnections)) {
461 if (!bluetoothInquirer.runDeviceInquiry()) {
462 if (stoped) {
463 break;
464 }
465 startTry++;
466 try {
467 Thread.sleep(Configuration.clientSleepOnDeviceInquiryError);
468 } catch (Exception e) {
469 break;
470 }
471 if (startTry < 3) {
472 continue;
473 }
474 Switcher.yield(this);
475 } else {
476 startTry = 0;
477 }
478 while (bluetoothInquirer.inquiring) {
479 try {
480 Thread.sleep(1000);
481 } catch (Exception e) {
482 break;
483 }
484 }
485 }
486 if ((Configuration.clientTestConnections) && (bluetoothInquirer.hasServers())) {
487 discoveryDryCount = 0;
488 discoverySuccessCount++;
489 lastSuccessfulDiscovery = System.currentTimeMillis();
490 if (!config.discoveryOnce) {
491 connectAndTest(bluetoothInquirer.serverURLs);
492 }
493 } else {
494 discoveryDryCount++;
495 if ((discoveryDryCount % 5 == 0) && (lastSuccessfulDiscovery != 0)) {
496 Logger.debug(config.logID + "No services " + discoveryDryCount + " times for "
497 + TimeUtils.secSince(lastSuccessfulDiscovery) + " " + discoverySuccessCount);
498 }
499 }
500 Logger.info(config.logID + "*Success:" + countSuccess + " Failure:" + failure.countFailure);
501 if ((countSuccess + failure.countFailure > 0) && (!Configuration.clientContinuous.booleanValue())) {
502 break;
503 }
504 if (stoped || config.discoveryOnce || config.connectOnce) {
505 break;
506 }
507 Switcher.yield(this);
508 }
509 } catch (Throwable e) {
510 if (!stoped) {
511 Logger.error(config.logID + "client error ", e);
512 }
513 } finally {
514 Switcher.clientEnds(this);
515 config.connectURL = null;
516 isRunning = false;
517 Logger.info(config.logID + "Client finished! " + TimeUtils.timeNowToString());
518 Switcher.yield(this);
519 }
520 }
521
522 public boolean isAnyServiceFound() {
523 if (bluetoothInquirer != null) {
524 return bluetoothInquirer.anyServicesFound;
525 } else {
526 return false;
527 }
528 }
529
530 public void shutdown() {
531 Logger.info(config.logID + "shutdownClient");
532 stoped = true;
533 if (bluetoothInquirer != null) {
534 bluetoothInquirer.shutdown();
535 }
536 if (RuntimeDetect.cldcStub != null) {
537 RuntimeDetect.cldcStub.interruptThread(thread);
538 }
539 Vector concurrentConnectionsCopy = CollectionUtils.copy(concurrentConnections);
540 for (Enumeration iter = concurrentConnectionsCopy.elements(); iter.hasMoreElements();) {
541 ConnectionHolder t = (ConnectionHolder) iter.nextElement();
542 t.shutdown();
543 }
544 }
545
546 public static void main(String[] args) {
547 JavaSECommon.initOnce();
548 try {
549 (new TestResponderClient()).run();
550
551 } catch (Throwable e) {
552 Logger.error("start error ", e);
553 }
554 }
555
556 }