[bluetooth] Add some utility classes (#9064)

* Add some utility classes that will be used by other bluetooth bindings.
* Add handle field to BluetoothDescriptor

Signed-off-by: Connor Petty <mistercpp2000+gitsignoff@gmail.com>
This commit is contained in:
Connor Petty
2020-11-23 02:34:39 -08:00
committed by GitHub
parent 38876647ad
commit 0b163f655c
10 changed files with 612 additions and 78 deletions

View File

@@ -396,7 +396,7 @@ public class BlueZBluetoothDevice extends BaseBluetoothDevice implements BlueZEv
for (BluetoothGattDescriptor dBusBlueZDescriptor : dBusBlueZCharacteristic.getGattDescriptors()) {
BluetoothDescriptor descriptor = new BluetoothDescriptor(characteristic,
UUID.fromString(dBusBlueZDescriptor.getUuid()));
UUID.fromString(dBusBlueZDescriptor.getUuid()), 0);
characteristic.addDescriptor(descriptor);
}
service.addCharacteristic(characteristic);

View File

@@ -12,9 +12,7 @@
*/
package org.openhab.binding.bluetooth.bluez.internal;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@@ -22,6 +20,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.freedesktop.dbus.exceptions.DBusException;
import org.openhab.binding.bluetooth.util.RetryException;
import org.openhab.binding.bluetooth.util.RetryFuture;
import org.openhab.core.common.ThreadPoolManager;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
@@ -71,7 +71,7 @@ public class DeviceManagerFactory {
public void initialize() {
logger.debug("initializing DeviceManagerFactory");
var stage1 = this.deviceManagerFuture = callAsync(() -> {
var stage1 = this.deviceManagerFuture = RetryFuture.callWithRetry(() -> {
try {
// if this is the first call to the library, this call
// should throw an exception (that we are catching)
@@ -83,12 +83,10 @@ public class DeviceManagerFactory {
}
}, scheduler);
stage1.thenCompose(devManager -> {
this.deviceManagerWrapperFuture = stage1.thenCompose(devManager -> {
// lambdas can't modify outside variables due to scoping, so instead we use an AtomicInteger.
AtomicInteger tryCount = new AtomicInteger();
// We need to set deviceManagerWrapperFuture here since we want to be able to cancel the underlying
// AsyncCompletableFuture instance
return this.deviceManagerWrapperFuture = callAsync(() -> {
return RetryFuture.callWithRetry(() -> {
int count = tryCount.incrementAndGet();
try {
logger.debug("Registering property handler attempt: {}", count);
@@ -127,60 +125,4 @@ public class DeviceManagerFactory {
}
this.deviceManagerWrapperFuture = null;
}
private static <T> CompletableFuture<T> callAsync(Callable<T> callable, ScheduledExecutorService scheduler) {
return new AsyncCompletableFuture<>(callable, scheduler);
}
// this is a utility class that allows use of Callable with CompletableFutures in a way such that the
// async future is cancellable thru this CompletableFuture instance.
private static class AsyncCompletableFuture<T> extends CompletableFuture<T> implements Runnable {
private final Callable<T> callable;
private final ScheduledExecutorService scheduler;
private final Object futureLock = new Object();
private Future<?> future;
public AsyncCompletableFuture(Callable<T> callable, ScheduledExecutorService scheduler) {
this.callable = callable;
this.scheduler = scheduler;
future = scheduler.submit(this);
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
synchronized (futureLock) {
future.cancel(mayInterruptIfRunning);
}
return super.cancel(mayInterruptIfRunning);
}
@Override
public void run() {
try {
complete(callable.call());
} catch (RetryException e) {
synchronized (futureLock) {
if (!future.isCancelled()) {
future = scheduler.schedule(this, e.delay, e.unit);
}
}
} catch (Exception e) {
completeExceptionally(e);
}
}
}
// this is a special exception to indicate to a AsyncCompletableFuture that the task needs to be retried.
private static class RetryException extends Exception {
private static final long serialVersionUID = 8512275408512109328L;
private long delay;
private TimeUnit unit;
public RetryException(long delay, TimeUnit unit) {
this.delay = delay;
this.unit = unit;
}
}
}