View Javadoc

1   /**
2    *  BlueCove - Java library for Bluetooth
3    *  Copyright (C) 2006-2007 Vlad Skarzhevskyy
4    * 
5    *  Licensed to the Apache Software Foundation (ASF) under one
6    *  or more contributor license agreements.  See the NOTICE file
7    *  distributed with this work for additional information
8    *  regarding copyright ownership.  The ASF licenses this file
9    *  to you under the Apache License, Version 2.0 (the
10   *  "License"); you may not use this file except in compliance
11   *  with the License.  You may obtain a copy of the License at
12   *
13   *    http://www.apache.org/licenses/LICENSE-2.0
14   *
15   *  Unless required by applicable law or agreed to in writing,
16   *  software distributed under the License is distributed on an
17   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18   *  KIND, either express or implied.  See the License for the
19   *  specific language governing permissions and limitations
20   *  under the License.
21   *
22   *  @author vlads
23   *  @version $Id: ClientConnectionThread.java 2607 2008-12-17 23:51:33Z skarzhevskyy $
24   */
25  package net.sf.bluecove.awt;
26  
27  import java.io.File;
28  import java.io.FileOutputStream;
29  import java.io.IOException;
30  import java.text.SimpleDateFormat;
31  import java.util.Date;
32  
33  import javax.bluetooth.L2CAPConnection;
34  import javax.microedition.io.Connection;
35  import javax.microedition.io.Connector;
36  import javax.microedition.io.StreamConnection;
37  
38  import org.bluecove.tester.log.Logger;
39  import org.bluecove.tester.util.IOUtils;
40  import org.bluecove.tester.util.RuntimeDetect;
41  import org.bluecove.tester.util.StringUtils;
42  import org.bluecove.tester.util.TimeUtils;
43  
44  import net.sf.bluecove.ConnectionHolder;
45  import net.sf.bluecove.ConnectionHolderL2CAP;
46  import net.sf.bluecove.ConnectionHolderStream;
47  import net.sf.bluecove.util.BluetoothTypesInfo;
48  
49  /**
50   *
51   * 
52   */
53  public class ClientConnectionThread extends Thread {
54  
55  	Object threadLocalBluetoothStack;
56  
57  	private static int connectionCount = 0;
58  
59  	private String serverURL;
60  
61  	private ConnectionHolder c;
62  
63  	private boolean stoped = false;
64  
65  	boolean isRunning = false;
66  
67  	boolean isConnecting = false;
68  
69  	long receivedCount = 0;
70  
71  	long reportedSize = 0;
72  
73  	long receivedPacketsCount = 0;
74  
75  	boolean rfcomm;
76  
77  	public static final int interpretDataChar = 0;
78  
79  	public static final int interpretDataCharArray = 1;
80  
81  	public static final int interpretDataStats = 2;
82  
83  	public static final int interpretDataStatsArray = 3;
84  
85  	public static final int interpretIgnore = 4;
86  
87  	int interpretData = interpretDataChar;
88  
89  	long reported = 0;
90  
91  	String logPrefix = "";
92  
93  	private StringBuffer dataBuf = new StringBuffer();
94  
95  	private boolean binaryData = false;
96  
97  	private FileOutputStream fileOut;
98  
99  	ClientConnectionThread(String serverURL) {
100 		super("ClientConnectionThread" + (++connectionCount));
101 		this.serverURL = serverURL;
102 	}
103 
104 	String getLocalBluetoothId() {
105 		if (threadLocalBluetoothStack == null) {
106 			return "";
107 		} else {
108 			return threadLocalBluetoothStack.toString();
109 		}
110 	}
111 
112 	public void run() {
113 		try {
114 			rfcomm = BluetoothTypesInfo.isRFCOMM(serverURL);
115 			if (!rfcomm && !BluetoothTypesInfo.isL2CAP(serverURL)) {
116 				Logger.error(logPrefix + "unsupported connection type " + serverURL);
117 				return;
118 			}
119 			RuntimeDetect.cldcStub.setThreadLocalBluetoothStack(threadLocalBluetoothStack);
120 			Connection conn = null;
121 			try {
122 				isConnecting = true;
123 				Logger.debug(logPrefix + "Connecting:" + serverURL + " ...");
124 				conn = Connector.open(serverURL);
125 			} catch (IOException e) {
126 				Logger.error(logPrefix + "Connection error", e);
127 				return;
128 			} finally {
129 				isConnecting = false;
130 			}
131 			Logger.info(logPrefix + "Connected");
132 			if (rfcomm) {
133 				ConnectionHolderStream cs = new ConnectionHolderStream((StreamConnection) conn);
134 				c = cs;
135 				cs.is = cs.conn.openInputStream();
136 				cs.os = cs.conn.openOutputStream();
137 				isRunning = true;
138 				while (!stoped) {
139 					if (interpretData == interpretIgnore) {
140 						Thread.sleep(777);
141 					} else if ((interpretData == interpretDataCharArray) || (interpretData == interpretDataStatsArray)) {
142 						byte b[] = new byte[0xFF];
143 						int readLen = cs.is.read(b);
144 						if (readLen == -1) {
145 							Logger.debug(logPrefix + "EOF recived");
146 							break;
147 						}
148 						receivedCount += readLen;
149 						for (int k = 0; k < readLen; k++) {
150 							printdataReceivedRFCOMM(b[k]);
151 						}
152 					} else {
153 						int data = cs.is.read();
154 						if (data == -1) {
155 							Logger.debug(logPrefix + "EOF recived");
156 							break;
157 						}
158 						receivedCount++;
159 						printdataReceivedRFCOMM(data);
160 					}
161 				}
162 				if (dataBuf.length() > 0) {
163 					Logger.debug(logPrefix + "cc:" + StringUtils.toBinaryText(dataBuf));
164 				}
165 			} else { // l2cap
166 				ConnectionHolderL2CAP lc = new ConnectionHolderL2CAP((L2CAPConnection) conn);
167 				isRunning = true;
168 				c = lc;
169 				while (!stoped) {
170 					if (interpretData == interpretIgnore) {
171 						Thread.sleep(777);
172 					} else {
173 						if ((interpretData != interpretDataCharArray) || (interpretData != interpretDataStatsArray)) {
174 							while ((!lc.channel.ready()) && (!stoped)) {
175 								Thread.sleep(100);
176 							}
177 						}
178 						if (stoped) {
179 							break;
180 						}
181 						int receiveMTU = lc.channel.getReceiveMTU();
182 						byte[] data = new byte[receiveMTU];
183 						int length = lc.channel.receive(data);
184 						receivedCount += length;
185 						receivedPacketsCount++;
186 						printdataReceivedL2CAP(data, length);
187 					}
188 				}
189 			}
190 		} catch (IOException e) {
191 			if (!stoped) {
192 				Logger.error(logPrefix + "Communication error", e);
193 			} else {
194 				Logger.debug(logPrefix + "communication stopped", e);
195 			}
196 		} catch (Throwable e) {
197 			Logger.error(logPrefix + "Error", e);
198 		} finally {
199 			isRunning = false;
200 			if (c != null) {
201 				c.shutdown();
202 			}
203 			closeFile();
204 		}
205 	}
206 
207 	private void printdataReceivedRFCOMM(int data) {
208 		switch (interpretData) {
209 		case interpretDataChar:
210 		case interpretDataCharArray:
211 			char c = (char) data;
212 			if ((!binaryData) && (c < ' ')) {
213 				binaryData = true;
214 			}
215 			dataBuf.append(c);
216 			if (((!binaryData) && (c == '\n')) || (dataBuf.length() > 32)) {
217 				Logger.debug("cc:" + StringUtils.toBinaryText(dataBuf));
218 				dataBuf = new StringBuffer();
219 			}
220 			break;
221 		case interpretDataStatsArray:
222 		case interpretDataStats:
223 			long now = System.currentTimeMillis();
224 			if (now - reported > 5 * 1000) {
225 				int size = (int) (receivedCount - reportedSize);
226 				reportedSize = receivedCount;
227 				Logger.debug(logPrefix + "Received " + receivedCount + " bytes " + TimeUtils.bps(size, reported));
228 				reported = now;
229 			}
230 			break;
231 		}
232 		synchronized (this) {
233 			if (fileOut != null) {
234 				try {
235 					fileOut.write((char) data);
236 				} catch (IOException e) {
237 					Logger.debug(logPrefix + "file write error", e);
238 					closeFile();
239 				}
240 			}
241 		}
242 	}
243 
244 	private void printdataReceivedL2CAP(byte[] data, int length) {
245 		switch (interpretData) {
246 		case interpretDataCharArray:
247 		case interpretDataChar:
248 			int messageLength = length;
249 			if ((length > 0) && (data[length - 1] == '\n')) {
250 				messageLength = length - 1;
251 			}
252 			StringBuffer buf = new StringBuffer();
253 			buf.append(logPrefix).append("cc:");
254 			if (messageLength != 0) {
255 				buf.append(StringUtils.toBinaryText(new StringBuffer(new String(data, 0, messageLength))));
256 			}
257 			buf.append(" (").append(length).append(")");
258 			Logger.debug(buf.toString());
259 			break;
260 		case interpretDataStatsArray:
261 		case interpretDataStats:
262 			long now = System.currentTimeMillis();
263 			if (now - reported > 5 * 1000) {
264 				int size = (int) (receivedCount - reportedSize);
265 				reportedSize = receivedCount;
266 				Logger.debug(logPrefix + "Received " + receivedPacketsCount + " packet(s), " + receivedCount
267 						+ " bytes " + TimeUtils.bps(size, reported));
268 				reported = now;
269 			}
270 			break;
271 		}
272 		synchronized (this) {
273 			if (fileOut != null) {
274 				try {
275 					fileOut.write(data, 0, length);
276 				} catch (IOException e) {
277 					Logger.debug(logPrefix + "file write error", e);
278 					closeFile();
279 				}
280 			}
281 		}
282 	}
283 
284 	public void shutdown() {
285 		stoped = true;
286 		if (c != null) {
287 			c.shutdown();
288 		}
289 		c = null;
290 		closeFile();
291 	}
292 
293 	private synchronized void closeFile() {
294 		if (fileOut != null) {
295 			try {
296 				fileOut.flush();
297 			} catch (IOException ignore) {
298 			}
299 			IOUtils.closeQuietly(fileOut);
300 			fileOut = null;
301 		}
302 	}
303 
304 	public void updateDataReceiveType(int type, boolean saveToFile) {
305 		interpretData = type;
306 
307 		if ((!saveToFile) && (fileOut != null)) {
308 			closeFile();
309 		} else if ((saveToFile) && (fileOut == null)) {
310 			SimpleDateFormat fmt = new SimpleDateFormat("MM-dd_HH-mm-ss");
311 			File file = new File("data-" + BluetoothTypesInfo.extractBluetoothAddress(serverURL)
312 					+ fmt.format(new Date()) + ".bin");
313 			try {
314 				fileOut = new FileOutputStream(file);
315 				Logger.info(logPrefix + "saving data to file " + file.getAbsolutePath());
316 			} catch (IOException e) {
317 			}
318 		}
319 	}
320 
321 	public void send(final byte data[]) {
322 		Thread t = new Thread("ClientConnectionSendThread" + (++connectionCount)) {
323 			public void run() {
324 				try {
325 					if (rfcomm) {
326 						((ConnectionHolderStream) c).os.write(data);
327 					} else {
328 						((ConnectionHolderL2CAP) c).channel.send(data);
329 					}
330 					Logger.debug(logPrefix + "data " + data.length + " sent");
331 				} catch (IOException e) {
332 					Logger.error(logPrefix + "Communication error", e);
333 				}
334 			}
335 		};
336 		t.setDaemon(true);
337 		t.start();
338 	}
339 }