summaryrefslogtreecommitdiffstats
path: root/examples/java
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2020-06-18 06:48:14 +0200
committerSven Gothel <[email protected]>2020-06-18 06:48:14 +0200
commit0ce725700a4e26e65f578fd1b1800d0bf85e289e (patch)
treebc7bdcf169d589cd13c0b10717bbaa9ea0a515eb /examples/java
parentb1126329e0bf84bb583027ca18b5e0e2a191e90e (diff)
Add debugging tool ScannerTinyB02; ScannerTinyB01 adds any device option.
Diffstat (limited to 'examples/java')
-rw-r--r--examples/java/CMakeLists.txt7
-rw-r--r--examples/java/ScannerTinyB01.java24
-rw-r--r--examples/java/ScannerTinyB02.java427
3 files changed, 448 insertions, 10 deletions
diff --git a/examples/java/CMakeLists.txt b/examples/java/CMakeLists.txt
index 17f3045e..3956a103 100644
--- a/examples/java/CMakeLists.txt
+++ b/examples/java/CMakeLists.txt
@@ -36,6 +36,13 @@ add_custom_command(TARGET ScannerTinyB01
COMMAND cp "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/ScannerTinyB01.dir/ScannerTinyB01.class" "${CMAKE_CURRENT_BINARY_DIR}"
)
+add_jar(ScannerTinyB02 SOURCES ScannerTinyB02.java INCLUDE_JARS "${CMAKE_CURRENT_BINARY_DIR}/../../java/tinyb2.jar" ENTRY_POINT Notification)
+
+add_custom_command(TARGET ScannerTinyB02
+ POST_BUILD
+ COMMAND cp "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/ScannerTinyB02.dir/ScannerTinyB02.class" "${CMAKE_CURRENT_BINARY_DIR}"
+)
+
add_jar(ScannerTinyB10 SOURCES ScannerTinyB10.java INCLUDE_JARS "${CMAKE_CURRENT_BINARY_DIR}/../../java/tinyb2.jar" ENTRY_POINT Notification)
add_custom_command(TARGET ScannerTinyB10
diff --git a/examples/java/ScannerTinyB01.java b/examples/java/ScannerTinyB01.java
index 0f392efe..9d6c8e18 100644
--- a/examples/java/ScannerTinyB01.java
+++ b/examples/java/ScannerTinyB01.java
@@ -59,6 +59,7 @@ public class ScannerTinyB01 {
int factory = 0;
int dev_id = 0; // default
int mode = 0;
+ int max_loops = 1;
boolean forever = false;
{
for(int i=0; i< args.length; i++) {
@@ -76,13 +77,12 @@ public class ScannerTinyB01 {
t0_discovery = Long.valueOf(args[++i]).longValue();
} else if( arg.equals("-forever") ) {
forever = true;
+ } else if( arg.equals("-loops") && args.length > (i+1) ) {
+ max_loops = Integer.valueOf(args[++i]).intValue();
}
}
- System.err.println("Run with '[-dev_id <adapter-index>] -mac <device_address> [-mode <mode>] [-factory <BluetoothManager-Factory-Implementation-Class>]'");
- if ( EUI48_ANY_DEVICE.equals(waitForDevice) ) {
- System.exit(-1);
- }
+ System.err.println("Run with '[-dev_id <adapter-index>] [-mac <device_address>] [-mode <mode>] [-factory <BluetoothManager-Factory-Implementation-Class>]'");
}
System.err.println("dev_id "+dev_id);
@@ -142,7 +142,7 @@ public class ScannerTinyB01 {
@Override
public void deviceFound(final BluetoothDevice device, final long timestamp) {
- final boolean matches = device.getAddress().equals(waitForDevice);
+ final boolean matches = EUI48_ANY_DEVICE.equals(waitForDevice) || device.getAddress().equals(waitForDevice);
System.err.println("****** FOUND__: "+device.toString()+" - match "+matches);
System.err.println("Status Adapter:");
System.err.println(device.getAdapter().toString());
@@ -157,13 +157,13 @@ public class ScannerTinyB01 {
@Override
public void deviceUpdated(final BluetoothDevice device, final EIRDataTypeSet updateMask, final long timestamp) {
- final boolean matches = device.getAddress().equals(waitForDevice);
+ final boolean matches = EUI48_ANY_DEVICE.equals(waitForDevice) || device.getAddress().equals(waitForDevice);
System.err.println("****** UPDATED: "+updateMask+" of "+device+" - match "+matches);
}
@Override
public void deviceConnected(final BluetoothDevice device, final long timestamp) {
- final boolean matches = device.getAddress().equals(waitForDevice);
+ final boolean matches = EUI48_ANY_DEVICE.equals(waitForDevice) || device.getAddress().equals(waitForDevice);
System.err.println("****** CONNECTED: "+device+" - matches "+matches);
}
@@ -202,7 +202,7 @@ public class ScannerTinyB01 {
int loop = 0;
try {
- do {
+ while( forever || loop < max_loops ) {
loop++;
System.err.println("****** Loop "+loop);
@@ -235,12 +235,15 @@ public class ScannerTinyB01 {
final List<BluetoothDevice> devices = adapter.getDevices();
for(final Iterator<BluetoothDevice> id = devices.iterator(); id.hasNext() && !timeout; ) {
final BluetoothDevice d = id.next();
- if(d.getAddress().equals(waitForDevice)) {
+ if( EUI48_ANY_DEVICE.equals(waitForDevice) || d.getAddress().equals(waitForDevice) ) {
sensor = d;
break;
}
+ }
+ if( null == sensor ) {
final long tn = BluetoothUtils.getCurrentMilliseconds();
timeout = ( tn - t0 ) > t0_discovery;
+ Thread.sleep(60);
}
}
}
@@ -251,6 +254,7 @@ public class ScannerTinyB01 {
}
System.err.println("Found device in "+(t1-t0)+" ms: ");
printDevice(sensor);
+
adapter.stopDiscovery();
final BooleanNotification connectedNotification = new BooleanNotification("Connected", t1);
@@ -322,7 +326,7 @@ public class ScannerTinyB01 {
}
sensor.disconnect();
System.err.println("ScannerTinyB01 04 ...: "+adapter);
- } while( forever );
+ }
} catch (final Throwable t) {
System.err.println("Caught: "+t.getMessage());
t.printStackTrace();
diff --git a/examples/java/ScannerTinyB02.java b/examples/java/ScannerTinyB02.java
new file mode 100644
index 00000000..764ba2f4
--- /dev/null
+++ b/examples/java/ScannerTinyB02.java
@@ -0,0 +1,427 @@
+/**
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.tinyb.AdapterSettings;
+import org.tinyb.BluetoothAdapter;
+import org.tinyb.BluetoothDevice;
+import org.tinyb.AdapterStatusListener;
+import org.tinyb.BluetoothException;
+import org.tinyb.BluetoothFactory;
+import org.tinyb.BluetoothGattCharacteristic;
+import org.tinyb.BluetoothGattService;
+import org.tinyb.BluetoothManager;
+import org.tinyb.BluetoothNotification;
+import org.tinyb.BluetoothUtils;
+import org.tinyb.EIRDataTypeSet;
+import org.tinyb.GATTCharacteristicListener;
+import org.tinyb.HCIStatusCode;
+
+/**
+ * Test and debugging application for certain situation.
+ * <p>
+ * Code will in 'in flux' and is not intended as an example.
+ * </p>
+ */
+public class ScannerTinyB02 {
+ static {
+ System.setProperty("org.tinyb.verbose", "true");
+ }
+ /** 10,000 milliseconds */
+ static long TO_DISCOVER = 10000;
+
+ static final String EUI48_ANY_DEVICE = "00:00:00:00:00:00";
+ static String waitForDevice = EUI48_ANY_DEVICE;
+
+ public static void main(final String[] args) throws InterruptedException {
+ final boolean waitForEnter=false;
+ long t0_discovery = TO_DISCOVER;
+ int factory = 0;
+ int dev_id = 0; // default
+ int mode = 3;
+ int max_loops = 5;
+ boolean forever = false;
+ {
+ for(int i=0; i< args.length; i++) {
+ final String arg = args[i];
+
+ if( arg.equals("-dev_id") && args.length > (i+1) ) {
+ dev_id = Integer.valueOf(args[++i]).intValue();
+ } else if( arg.equals("-mac") && args.length > (i+1) ) {
+ waitForDevice = args[++i];
+ } else if( arg.equals("-mode") && args.length > (i+1) ) {
+ mode = Integer.valueOf(args[++i]).intValue();
+ } else if( arg.equals("-factory") && args.length > (i+1) ) {
+ factory = Integer.valueOf(args[++i]).intValue();
+ } else if( arg.equals("-t0_discovery") && args.length > (i+1) ) {
+ t0_discovery = Long.valueOf(args[++i]).longValue();
+ } else if( arg.equals("-forever") ) {
+ forever = true;
+ } else if( arg.equals("-loops") && args.length > (i+1) ) {
+ max_loops = Integer.valueOf(args[++i]).intValue();
+ }
+ }
+
+ System.err.println("Run with '[-dev_id <adapter-index>] [-mac <device_address>] [-mode <mode>] [-factory <BluetoothManager-Factory-Implementation-Class>]'");
+ }
+
+ System.err.println("dev_id "+dev_id);
+ System.err.println("waitForDevice: "+waitForDevice);
+
+ if( waitForEnter ) {
+ System.err.println("Press ENTER to continue\n");
+ try{ System.in.read();
+ } catch(final Exception e) { }
+ }
+
+ final BluetoothFactory.ImplementationIdentifier implID = 0 == factory ? BluetoothFactory.DirectBTImplementationID : BluetoothFactory.DBusImplementationID;
+ final BluetoothManager manager;
+ {
+ BluetoothManager _manager = null;
+ try {
+ _manager = BluetoothFactory.getBluetoothManager( implID );
+ } catch (BluetoothException | NoSuchMethodException | SecurityException
+ | IllegalAccessException | IllegalArgumentException
+ | InvocationTargetException | ClassNotFoundException e) {
+ System.err.println("Unable to instantiate BluetoothManager via "+implID);
+ e.printStackTrace();
+ System.exit(-1);
+ }
+ manager = _manager;
+ }
+ final BluetoothAdapter adapter;
+ {
+ final List<BluetoothAdapter> adapters = manager.getAdapters();
+ for(int i=0; i < adapters.size(); i++) {
+ System.err.println("Adapter["+i+"]: "+adapters.get(i));
+ }
+ if( adapters.size() <= dev_id ) {
+ System.err.println("No adapter dev_id "+dev_id+" available, adapter count "+adapters.size());
+ System.exit(-1);
+ }
+ adapter = adapters.get(dev_id);
+ }
+
+ final BluetoothDevice[] matchingDiscoveredDeviceBucket = { null };
+
+ final AdapterStatusListener statusListener = new AdapterStatusListener() {
+ @Override
+ public void adapterSettingsChanged(final BluetoothAdapter adapter, final AdapterSettings oldmask,
+ final AdapterSettings newmask, final AdapterSettings changedmask, final long timestamp) {
+ System.err.println("****** SETTINGS: "+oldmask+" -> "+newmask+", changed "+changedmask);
+ System.err.println("Status Adapter:");
+ System.err.println(adapter.toString());
+ }
+
+ @Override
+ public void discoveringChanged(final BluetoothAdapter adapter, final boolean enabled, final boolean keepAlive, final long timestamp) {
+ System.err.println("****** DISCOVERING: enabled "+enabled+", keepAlive "+keepAlive+" on "+adapter);
+ System.err.println("Status Adapter:");
+ System.err.println(adapter.toString());
+ }
+
+ @Override
+ public void deviceFound(final BluetoothDevice device, final long timestamp) {
+ final boolean matches = EUI48_ANY_DEVICE.equals(waitForDevice) || device.getAddress().equals(waitForDevice);
+ System.err.println("****** FOUND__: "+device.toString()+" - match "+matches);
+ System.err.println("Status Adapter:");
+ System.err.println(device.getAdapter().toString());
+
+ if( matches ) {
+ synchronized(matchingDiscoveredDeviceBucket) {
+ matchingDiscoveredDeviceBucket[0] = device;
+ matchingDiscoveredDeviceBucket.notifyAll();
+ }
+ }
+ }
+
+ @Override
+ public void deviceUpdated(final BluetoothDevice device, final EIRDataTypeSet updateMask, final long timestamp) {
+ final boolean matches = EUI48_ANY_DEVICE.equals(waitForDevice) || device.getAddress().equals(waitForDevice);
+ System.err.println("****** UPDATED: "+updateMask+" of "+device+" - match "+matches);
+ }
+
+ @Override
+ public void deviceConnected(final BluetoothDevice device, final long timestamp) {
+ final boolean matches = EUI48_ANY_DEVICE.equals(waitForDevice) || device.getAddress().equals(waitForDevice);
+ System.err.println("****** CONNECTED: "+device+" - matches "+matches);
+ }
+
+ @Override
+ public void deviceDisconnected(final BluetoothDevice device, final HCIStatusCode reason, final long timestamp) {
+ System.err.println("****** DISCONNECTED: Reason "+reason+": "+device+" on "+device.getAdapter());
+ }
+ };
+ adapter.addStatusListener(statusListener, null);
+
+ final long timestamp_t0 = BluetoothUtils.getCurrentMilliseconds();
+
+ adapter.enableDiscoverableNotifications(new BooleanNotification("Discoverable", timestamp_t0));
+
+ adapter.enableDiscoveringNotifications(new BooleanNotification("Discovering", timestamp_t0));
+
+ adapter.enablePairableNotifications(new BooleanNotification("Pairable", timestamp_t0));
+
+ adapter.enablePoweredNotifications(new BooleanNotification("Powered", timestamp_t0));
+
+ final GATTCharacteristicListener myCharacteristicListener = new GATTCharacteristicListener() {
+ @Override
+ public void notificationReceived(final BluetoothGattCharacteristic charDecl,
+ final byte[] value, final long timestamp) {
+ System.err.println("****** GATT notificationReceived: "+charDecl+
+ ", value "+BluetoothUtils.bytesHexString(value, true, true));
+ }
+
+ @Override
+ public void indicationReceived(final BluetoothGattCharacteristic charDecl,
+ final byte[] value, final long timestamp, final boolean confirmationSent) {
+ System.err.println("****** GATT indicationReceived: "+charDecl+
+ ", value "+BluetoothUtils.bytesHexString(value, true, true));
+ }
+ };
+
+ int loop = 0;
+ try {
+ while( forever || loop < max_loops ) {
+ loop++;
+ System.err.println("****** Loop "+loop);
+
+ final long t0 = BluetoothUtils.getCurrentMilliseconds();
+
+ final boolean discoveryStarted = true; // adapter.startDiscovery(true);
+ {
+ final Thread lalaTask = new Thread( new Runnable() {
+ @Override
+ public void run() {
+ adapter.startDiscovery(true);
+ }
+ }, "lala");
+ lalaTask.setDaemon(true); // detach thread
+ lalaTask.start();
+ }
+
+ System.err.println("The discovery started: " + (discoveryStarted ? "true" : "false") + " for mac "+waitForDevice+", mode "+mode);
+ if( !discoveryStarted ) {
+ break;
+ }
+ BluetoothDevice sensor = null;
+
+ if( 0 == mode ) {
+ synchronized(matchingDiscoveredDeviceBucket) {
+ boolean timeout = false;
+ while( !timeout && null == matchingDiscoveredDeviceBucket[0] ) {
+ matchingDiscoveredDeviceBucket.wait(t0_discovery);
+ final long tn = BluetoothUtils.getCurrentMilliseconds();
+ timeout = ( tn - t0 ) > t0_discovery;
+ }
+ sensor = matchingDiscoveredDeviceBucket[0];
+ matchingDiscoveredDeviceBucket[0] = null;
+ }
+ } else if( 1 == mode ) {
+ sensor = adapter.find(null, waitForDevice, t0_discovery);
+ } else {
+ boolean timeout = false;
+ while( null == sensor && !timeout ) {
+ final List<BluetoothDevice> devices = adapter.getDevices();
+ int i=0;
+ for(final Iterator<BluetoothDevice> id = devices.iterator(); id.hasNext() && !timeout; ) {
+ final BluetoothDevice d = id.next();
+ final boolean match = EUI48_ANY_DEVICE.equals(waitForDevice) || d.getAddress().equals(waitForDevice);
+ System.err.println("****** Has "+i+"/"+devices.size()+": match "+match+": "+d.toString());
+ i++;
+ if( match ) {
+ sensor = d;
+ break;
+ }
+ }
+ if( null == sensor ) {
+ final long tn = BluetoothUtils.getCurrentMilliseconds();
+ timeout = ( tn - t0 ) > t0_discovery;
+ System.err.print(".");
+ Thread.sleep(60);
+ }
+ }
+ }
+ final long t1 = BluetoothUtils.getCurrentMilliseconds();
+ if (sensor == null) {
+ System.err.println("No sensor found within "+(t1-t0)+" ms");
+ continue; // forever loop
+ }
+ System.err.println("Found device in "+(t1-t0)+" ms: ");
+ printDevice(sensor);
+
+ // adapter.stopDiscovery();
+ {
+ final Thread lalaTask = new Thread( new Runnable() {
+ @Override
+ public void run() {
+ adapter.stopDiscovery();
+ }
+ }, "lala");
+ lalaTask.setDaemon(true); // detach thread
+ lalaTask.start();
+ }
+
+ final BooleanNotification connectedNotification = new BooleanNotification("Connected", t1);
+ final BooleanNotification servicesResolvedNotification = new BooleanNotification("ServicesResolved", t1);
+ sensor.enableConnectedNotifications(connectedNotification);
+ sensor.enableServicesResolvedNotifications(servicesResolvedNotification);
+
+ final long t2 = BluetoothUtils.getCurrentMilliseconds();
+ final long t3;
+ if ( sensor.connect() ) {
+ t3 = BluetoothUtils.getCurrentMilliseconds();
+ System.err.println("Sensor connected: "+(t3-t2)+" ms, total "+(t3-t0)+" ms");
+ System.err.println("Sensor connectedNotification: "+connectedNotification.getValue());
+ } else {
+ t3 = BluetoothUtils.getCurrentMilliseconds();
+ System.out.println("Could not connect device: "+(t3-t2)+" ms, total "+(t3-t0)+" ms");
+ // we tolerate the failed immediate connect, as it might happen at a later time
+ }
+
+ synchronized( servicesResolvedNotification ) {
+ while( !servicesResolvedNotification.getValue() ) {
+ final long tn = BluetoothUtils.getCurrentMilliseconds();
+ if( tn - t3 > 20000 ) {
+ break; // 20s TO
+ }
+ servicesResolvedNotification.wait();
+ }
+ }
+ final long t4;
+ if ( servicesResolvedNotification.getValue() ) {
+ t4 = BluetoothUtils.getCurrentMilliseconds();
+ System.err.println("Sensor servicesResolved: "+(t4-t3)+" ms, total "+(t4-t0)+" ms");
+ } else {
+ t4 = BluetoothUtils.getCurrentMilliseconds();
+ System.out.println("Could not connect device: "+(t4-t3)+" ms, total "+(t4-t0)+" ms");
+ System.exit(-1);
+ }
+
+ if( true ) {
+ final BluetoothDevice _sensor = sensor;
+ final Thread lalaTask = new Thread( new Runnable() {
+ @Override
+ public void run() {
+ _sensor.disconnect();
+ }
+ }, "lala");
+ lalaTask.setDaemon(true); // detach thread
+ lalaTask.start();
+
+ // Thread.sleep(60);
+ // sensor.connect();
+ continue;
+ } else {
+ final List<BluetoothGattService> primServices = sensor.getServices();
+ if ( null == primServices || primServices.isEmpty() ) {
+ System.err.println("No BluetoothGattService found!");
+ } else {
+ final boolean addedCharacteristicListenerRes =
+ BluetoothGattService.addCharacteristicListenerToAll(sensor, primServices, myCharacteristicListener);
+ System.err.println("Added GATTCharacteristicListener: "+addedCharacteristicListenerRes);
+
+ int i=0, j=0;
+ for(final Iterator<BluetoothGattService> srvIter = primServices.iterator(); srvIter.hasNext(); i++) {
+ final BluetoothGattService primService = srvIter.next();
+ System.err.printf(" [%02d] Service %s\n", i, primService.toString());
+ System.err.printf(" [%02d] Service Characteristics\n", i);
+ final List<BluetoothGattCharacteristic> serviceCharacteristics = primService.getCharacteristics();
+ for(final Iterator<BluetoothGattCharacteristic> charIter = serviceCharacteristics.iterator(); charIter.hasNext(); j++) {
+ final BluetoothGattCharacteristic serviceChar = charIter.next();
+ System.err.printf(" [%02d.%02d] Decla: %s\n", i, j, serviceChar.toString());
+ final List<String> properties = Arrays.asList(serviceChar.getFlags());
+ if( properties.contains("read") ) {
+ final byte[] value = serviceChar.readValue();
+ final String svalue = BluetoothUtils.decodeUTF8String(value, 0, value.length);
+ System.err.printf(" [%02d.%02d] Value: %s ('%s')\n",
+ i, j, BluetoothUtils.bytesHexString(value, true, true), svalue);
+ }
+ }
+ }
+ Thread.sleep(1000); // FIXME: Wait for notifications
+
+ final boolean remRes = BluetoothGattService.removeCharacteristicListenerFromAll(sensor, primServices, myCharacteristicListener);
+ System.err.println("Removed GATTCharacteristicListener: "+remRes);
+ }
+ sensor.disconnect();
+ System.err.println("ScannerTinyB01 04 ...: "+adapter);
+ }
+ }
+ } catch (final Throwable t) {
+ System.err.println("Caught: "+t.getMessage());
+ t.printStackTrace();
+ }
+
+ System.err.println("ScannerTinyB01 02 clear listener etc .. ");
+ adapter.removeStatusListener(statusListener);
+ adapter.disableDiscoverableNotifications();
+ adapter.disableDiscoveringNotifications();
+ adapter.disablePairableNotifications();
+ adapter.disablePoweredNotifications();
+
+ System.err.println("ScannerTinyB01 03 close: "+adapter);
+ adapter.close();
+ System.err.println("ScannerTinyB01 04");
+ manager.shutdown();
+ System.err.println("ScannerTinyB01 XX");
+ }
+ private static void printDevice(final BluetoothDevice device) {
+ System.err.println("Address = " + device.getAddress());
+ System.err.println(" Name = " + device.getName());
+ System.err.println(" Connected = " + device.getConnected());
+ System.err.println();
+ }
+ static class BooleanNotification implements BluetoothNotification<Boolean> {
+ private final long t0;
+ private final String name;
+ private boolean v;
+
+ public BooleanNotification(final String name, final long t0) {
+ this.t0 = t0;
+ this.name = name;
+ this.v = false;
+ }
+
+ @Override
+ public void run(final Boolean v) {
+ synchronized(this) {
+ final long t1 = BluetoothUtils.getCurrentMilliseconds();
+ this.v = v.booleanValue();
+ System.out.println("###### "+name+": "+v+" in td "+(t1-t0)+" ms!");
+ this.notifyAll();
+ }
+ }
+ public boolean getValue() {
+ synchronized(this) {
+ return v;
+ }
+ }
+ }
+}