View Javadoc

1   /**
2    *  BlueCove - Java library for Bluetooth
3    *  Copyright (C) 2006-2008 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: TestResponderServerOBEX.java 2607 2008-12-17 23:51:33Z skarzhevskyy $
24   */
25  package net.sf.bluecove;
26  
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.io.InterruptedIOException;
30  import java.io.OutputStream;
31  import java.util.Date;
32  import java.util.Timer;
33  import java.util.TimerTask;
34  
35  import javax.bluetooth.DataElement;
36  import javax.bluetooth.LocalDevice;
37  import javax.bluetooth.RemoteDevice;
38  import javax.bluetooth.ServiceRecord;
39  import javax.bluetooth.UUID;
40  import javax.microedition.io.Connection;
41  import javax.microedition.io.Connector;
42  import javax.obex.HeaderSet;
43  import javax.obex.Operation;
44  import javax.obex.ResponseCodes;
45  import javax.obex.ServerRequestHandler;
46  import javax.obex.SessionNotifier;
47  
48  import org.bluecove.tester.log.Logger;
49  import org.bluecove.tester.util.IOUtils;
50  import org.bluecove.tester.util.RuntimeDetect;
51  import org.bluecove.tester.util.StringUtils;
52  import org.bluecove.tester.util.TimeUtils;
53  
54  import net.sf.bluecove.util.BluetoothTypesInfo;
55  
56  public class TestResponderServerOBEX implements Runnable {
57  
58  	private SessionNotifier serverConnection;
59  
60  	private boolean isStoped = false;
61  
62  	private boolean isRunning = false;
63  
64  	private Thread thread;
65  
66  	private Object threadLocalBluetoothStack;
67  
68  	public static final UUID OBEX_OBJECT_PUSH = new UUID(0x1105);
69  
70  	public static final String OBEX_OBJECT_PUSH_SERVER_NAME = "OBEX Object Push";
71  
72  	private TestResponderServerOBEX() {
73  
74  	}
75  
76  	public static TestResponderServerOBEX startServer() {
77  		TestResponderServerOBEX srv = new TestResponderServerOBEX();
78  		srv.threadLocalBluetoothStack = Configuration.threadLocalBluetoothStack;
79  		srv.thread = RuntimeDetect.cldcStub.createNamedThread(srv, "ServerOBEX");
80  		srv.thread.start();
81  		return srv;
82  	}
83  
84  	public boolean isRunning() {
85  		return isRunning;
86  	}
87  
88  	public void run() {
89  		isStoped = false;
90  		boolean deviceServiceClassesUpdated = false;
91  		RuntimeDetect.cldcStub.setThreadLocalBluetoothStack(threadLocalBluetoothStack);
92  		LocalDevice localDevice;
93  		try {
94  			localDevice = LocalDevice.getLocalDevice();
95  			if (Configuration.testServerOBEX_TCP.booleanValue()) {
96  				serverConnection = (SessionNotifier) Connector
97  						.open(BluetoothTypesInfo.PROTOCOL_SCHEME_TCP_OBEX + "://");
98  			} else {
99  				StringBuffer url = new StringBuffer(BluetoothTypesInfo.PROTOCOL_SCHEME_BT_OBEX);
100 				url.append("://localhost:");
101 				if (Configuration.testServerOBEXObjectPush) {
102 					url.append(OBEX_OBJECT_PUSH);
103 					url.append(";name=");
104 					url.append(OBEX_OBJECT_PUSH_SERVER_NAME);
105 				} else {
106 					url.append(Configuration.blueCoveOBEXUUID());
107 					url.append(";name=");
108 					url.append(Consts.RESPONDER_SERVERNAME);
109 					url.append("_ox");
110 				}
111 
112 				url.append(Configuration.serverURLParams());
113 
114 				serverConnection = (SessionNotifier) Connector.open(url.toString());
115 				if (Configuration.testServerOBEXObjectPush) {
116 					try {
117 						ServiceRecord record = localDevice.getRecord(serverConnection);
118 
119 						final int OBJECT_TRANSFER_SERVICE = 0x100000;
120 
121 						try {
122 							record.setDeviceServiceClasses(OBJECT_TRANSFER_SERVICE);
123 						} catch (Throwable e) {
124 							Logger.debug("setDeviceServiceClasses", e);
125 						}
126 
127 						DataElement bluetoothProfileDescriptorList = new DataElement(DataElement.DATSEQ);
128 						DataElement obbexPushProfileDescriptor = new DataElement(DataElement.DATSEQ);
129 						obbexPushProfileDescriptor.addElement(new DataElement(DataElement.UUID, OBEX_OBJECT_PUSH));
130 						obbexPushProfileDescriptor.addElement(new DataElement(DataElement.U_INT_2, 0x100));
131 						bluetoothProfileDescriptorList.addElement(obbexPushProfileDescriptor);
132 						record.setAttributeValue(0x0009, bluetoothProfileDescriptorList);
133 
134 						final short ATTR_SUPPORTED_FORMAT_LIST_LIST = 0x0303;
135 						DataElement supportedFormatList = new DataElement(DataElement.DATSEQ);
136 						// any type of object.
137 						supportedFormatList.addElement(new DataElement(DataElement.U_INT_1, 0xFF));
138 						record.setAttributeValue(ATTR_SUPPORTED_FORMAT_LIST_LIST, supportedFormatList);
139 
140 						final short UUID_PUBLICBROWSE_GROUP = 0x1002;
141 						final short ATTR_BROWSE_GRP_LIST = 0x0005;
142 						DataElement browseClassIDList = new DataElement(DataElement.DATSEQ);
143 						UUID browseClassUUID = new UUID(UUID_PUBLICBROWSE_GROUP);
144 						browseClassIDList.addElement(new DataElement(DataElement.UUID, browseClassUUID));
145 						record.setAttributeValue(ATTR_BROWSE_GRP_LIST, browseClassIDList);
146 
147 						localDevice.updateRecord(record);
148 					} catch (Throwable e) {
149 						Logger.error("OBEX Service Updating SDP", e);
150 					}
151 				} else if (Configuration.testServiceAttributes.booleanValue()) {
152 					ServiceRecord record = localDevice.getRecord(serverConnection);
153 					if (record == null) {
154 						Logger.warn("Bluetooth ServiceRecord is null");
155 					} else {
156 						TestResponderServer.buildServiceRecord(record);
157 
158 						try {
159 							record.setDeviceServiceClasses(BluetoothTypesInfo.DeviceClassConsts.INFORMATION_SERVICE);
160 							deviceServiceClassesUpdated = true;
161 						} catch (Throwable e) {
162 							if (e.getMessage().startsWith("Not Supported on")) {
163 								Logger.error("setDeviceServiceClasses " + e.getMessage());
164 							} else {
165 								Logger.error("setDeviceServiceClasses", e);
166 							}
167 						}
168 
169 						try {
170 							LocalDevice.getLocalDevice().updateRecord(record);
171 							Logger.debug("OBEX ServiceRecord updated");
172 						} catch (Throwable e) {
173 							Logger.error("OBEX Service Record update error", e);
174 						}
175 					}
176 				}
177 			}
178 		} catch (Throwable e) {
179 			Logger.error("OBEX Server start error", e);
180 			isStoped = true;
181 			return;
182 		}
183 
184 		if (deviceServiceClassesUpdated) {
185 			Logger.info("DeviceClass:" + BluetoothTypesInfo.toString(localDevice.getDeviceClass()));
186 		}
187 
188 		try {
189 			int errorCount = 0;
190 			int count = 0;
191 			isRunning = true;
192 			boolean showServiceRecordOnce = true;
193 			while (!isStoped) {
194 				RequestHandler handler = new RequestHandler();
195 				try {
196 					count++;
197 					Logger.info("Accepting OBEX connections");
198 					if (showServiceRecordOnce) {
199 						try {
200 							Logger.debug("OxUrl:"
201 									+ localDevice.getRecord(serverConnection).getConnectionURL(
202 											Configuration.getRequiredSecurity(), false));
203 						} catch (IllegalArgumentException e) {
204 							Logger.debug("Can't get local serviceRecord", e);
205 						}
206 						showServiceRecordOnce = false;
207 					}
208 					if (Configuration.authenticateOBEX.getValue() != 0) {
209 						handler.auth = new OBEXTestAuthenticator("server" + count);
210 						handler.connectionAccepted(serverConnection.acceptAndOpen(handler, handler.auth));
211 					} else {
212 						handler.connectionAccepted(serverConnection.acceptAndOpen(handler));
213 					}
214 				} catch (InterruptedIOException e) {
215 					isStoped = true;
216 					break;
217 				} catch (Throwable e) {
218 					if (errorCount > 3) {
219 						isStoped = true;
220 					}
221 					if (isStoped) {
222 						return;
223 					}
224 					errorCount++;
225 					Logger.error("acceptAndOpen ", e);
226 					continue;
227 				}
228 				errorCount = 0;
229 			}
230 		} finally {
231 			close();
232 			Logger.info("OBEX Server finished! " + TimeUtils.timeNowToString());
233 			isRunning = false;
234 		}
235 	}
236 
237 	void close() {
238 		try {
239 			if (serverConnection != null) {
240 				serverConnection.close();
241 			}
242 			Logger.debug("OBEX ServerConnection closed");
243 		} catch (Throwable e) {
244 			Logger.error("OBEX Server stop error", e);
245 		}
246 	}
247 
248 	void closeServer() {
249 		isStoped = true;
250 		close();
251 	}
252 
253 	/*
254 	 * We testing on Java 1.1 and Timer is not important
255 	 */
256 	private class NoTimeWrapper {
257 
258 		Object timer;
259 
260 		NoTimeWrapper() {
261 			try {
262 				timer = new Timer();
263 			} catch (Throwable e) {
264 				Logger.warn("OBEX Server has no timer");
265 			}
266 		}
267 
268 		void schedule(final RequestHandler handler) {
269 			if (timer != null) {
270 				((Timer) timer).schedule(new TimerTask() {
271 					public void run() {
272 						handler.notConnectedClose();
273 					}
274 				}, 1000 * 30);
275 			}
276 		}
277 
278 		void cancel() {
279 			if (timer != null) {
280 				((Timer) timer).cancel();
281 			}
282 		}
283 	}
284 
285 	private class RequestHandler extends ServerRequestHandler {
286 
287 		OBEXTestAuthenticator auth;
288 
289 		NoTimeWrapper notConnectedTimer = new NoTimeWrapper();
290 
291 		boolean isConnected = false;
292 
293 		Connection cconn;
294 
295 		RemoteDevice remoteDevice;
296 
297 		void connectionAccepted(Connection cconn) {
298 			Logger.info("Received OBEX connection");
299 			this.cconn = cconn;
300 			if (!Configuration.testServerOBEX_TCP.booleanValue()) {
301 				try {
302 					remoteDevice = RemoteDevice.getRemoteDevice(cconn);
303 					Logger.debug("connected toBTAddress " + remoteDevice.getBluetoothAddress());
304 				} catch (IOException e) {
305 					Logger.error("OBEX Server error", e);
306 				}
307 			}
308 			if (!isConnected) {
309 				notConnectedTimer.schedule(this);
310 			}
311 		}
312 
313 		void notConnectedClose() {
314 			if (!isConnected) {
315 				Logger.debug("OBEX connection timeout");
316 				IOUtils.closeQuietly(cconn);
317 			}
318 		}
319 
320 		private void debugHeaderSet(HeaderSet headers) {
321 			if (headers == null) {
322 				return;
323 			}
324 			try {
325 				int[] headerIDArray = headers.getHeaderList();
326 				if (headerIDArray == null) {
327 					return;
328 				}
329 				Logger.debug("Headers.length:" + headerIDArray.length);
330 
331 				for (int i = 0; i < headerIDArray.length; i++) {
332 					int hi = headerIDArray[i];
333 					Object value = headers.getHeader(hi);
334 					Logger.debug("h[" + hi + "]=" + value);
335 				}
336 			} catch (IOException e) {
337 				Logger.error("headers", e);
338 			}
339 		}
340 
341 		public int onConnect(HeaderSet request, HeaderSet reply) {
342 			isConnected = true;
343 			notConnectedTimer.cancel();
344 			Logger.debug("OBEX onConnect");
345 			debugHeaderSet(request);
346 			if (Configuration.authenticate.booleanValue()) {
347 				if (!remoteDevice.isAuthenticated()) {
348 					return ResponseCodes.OBEX_HTTP_FORBIDDEN;
349 				}
350 				Logger.debug("OBEX connection Authenticated");
351 			}
352 			return ResponseCodes.OBEX_HTTP_OK;
353 		}
354 
355 		public void onDisconnect(HeaderSet request, HeaderSet reply) {
356 			Logger.debug("OBEX onDisconnect");
357 			debugHeaderSet(request);
358 		}
359 
360 		public int onSetPath(HeaderSet request, HeaderSet reply, boolean backup, boolean create) {
361 			Logger.debug("OBEX onSetPath");
362 			debugHeaderSet(request);
363 			return super.onSetPath(request, reply, backup, create);
364 		}
365 
366 		public int onDelete(HeaderSet request, HeaderSet reply) {
367 			Logger.debug("OBEX onDelete");
368 			debugHeaderSet(request);
369 			return super.onDelete(request, reply);
370 		}
371 
372 		public int onPut(Operation op) {
373 			Logger.debug("OBEX onPut");
374 			try {
375 				HeaderSet hs = op.getReceivedHeaders();
376 				debugHeaderSet(hs);
377 				String name = (String) hs.getHeader(HeaderSet.NAME);
378 				if (name != null) {
379 					Logger.debug("name:" + name);
380 
381 					HeaderSet sendHeaders = createHeaderSet();
382 					sendHeaders.setHeader(HeaderSet.DESCRIPTION, name);
383 					op.sendHeaders(sendHeaders);
384 				}
385 
386 				InputStream is = op.openInputStream();
387 
388 				StringBuffer buf = new StringBuffer();
389 				while (!isStoped) {
390 					int data = is.read();
391 					if (data == -1) {
392 						Logger.debug("EOS recived");
393 						break;
394 					}
395 					char c = (char) data;
396 					buf.append(c);
397 					if ((c == '\n') || (buf.length() > 30)) {
398 						Logger.debug("cc:" + StringUtils.toBinaryText(buf));
399 						buf = new StringBuffer();
400 					}
401 				}
402 				if (buf.length() > 0) {
403 					Logger.debug("cc:" + StringUtils.toBinaryText(buf));
404 				}
405 				op.close();
406 				return ResponseCodes.OBEX_HTTP_OK;
407 			} catch (IOException e) {
408 				Logger.error("OBEX Server onPut error", e);
409 				return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
410 			} finally {
411 				Logger.debug("OBEX onPut ends");
412 			}
413 		}
414 
415 		public int onGet(Operation op) {
416 			Logger.debug("OBEX onGet");
417 			String message = "Hello client! now " + new Date().toString();
418 			try {
419 				HeaderSet hs = op.getReceivedHeaders();
420 				debugHeaderSet(hs);
421 				String name = (String) hs.getHeader(HeaderSet.NAME);
422 
423 				if (name != null) {
424 					message += "\nYou ask for [" + name + "]";
425 				}
426 				byte[] messageBytes = message.getBytes();
427 				if (name != null) {
428 					HeaderSet sendHeaders = createHeaderSet();
429 					sendHeaders.setHeader(HeaderSet.DESCRIPTION, name);
430 					sendHeaders.setHeader(HeaderSet.LENGTH, new Long(messageBytes.length));
431 					op.sendHeaders(sendHeaders);
432 				}
433 
434 				OutputStream os = op.openOutputStream();
435 				os.write(messageBytes);
436 				os.flush();
437 				os.close();
438 				op.close();
439 				return ResponseCodes.OBEX_HTTP_OK;
440 			} catch (IOException e) {
441 				Logger.error("OBEX Server onGet error", e);
442 				return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
443 			} finally {
444 				Logger.debug("OBEX onGet ends");
445 			}
446 		}
447 
448 		public void onAuthenticationFailure(byte[] userName) {
449 			Logger.debug("OBEX AuthFailure " + new String(userName));
450 		}
451 
452 	}
453 }