]> git.basschouten.com Git - openhab-addons.git/commitdiff
[knx] Improve thread safety, null-analysis (#14509)
authorHolger Friedrich <holgerfriedrich@users.noreply.github.com>
Mon, 6 Mar 2023 21:34:16 +0000 (22:34 +0100)
committerGitHub <noreply@github.com>
Mon, 6 Mar 2023 21:34:16 +0000 (22:34 +0100)
Carryover from smarthomej/addons#13 and smarthomej/addons#46, smarthomej/addons#60.

Also-by: Jan N. Klug <github@klug.nrw>
Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/KNXTypeMapper.java
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/channel/TypeDimmer.java
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/channel/TypeRollershutter.java
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/AbstractKNXClient.java
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/KNXCoreTypeMapper.java
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/factory/KNXHandlerFactory.java
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/AbstractKNXThingHandler.java
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/DeviceThingHandler.java
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/IPBridgeThingHandler.java
bundles/org.openhab.binding.knx/src/test/java/org/openhab/binding/knx/internal/channel/KNXChannelTypeTest.java

index 9c09850c317233b1258853e09e1cb367da453c53..252e507dd7e953222ee5a7ee89116f54959c563c 100644 (file)
@@ -38,7 +38,7 @@ public interface KNXTypeMapper {
      * @return datapoint value as a string
      */
     @Nullable
-    public String toDPTValue(Type type, @Nullable String dpt);
+    String toDPTValue(Type type, @Nullable String dpt);
 
     /**
      * maps a datapoint value to an openHAB command or state
@@ -49,8 +49,8 @@ public interface KNXTypeMapper {
      * @return a command or state of openHAB
      */
     @Nullable
-    public Type toType(Datapoint datapoint, byte[] data);
+    Type toType(Datapoint datapoint, byte[] data);
 
     @Nullable
-    public Class<? extends Type> toTypeClass(@Nullable String dpt);
+    Class<? extends Type> toTypeClass(@Nullable String dpt);
 }
index 90e55672fda124ea3ac1f57975188de7f0b35a14..5e01f0386ba8d528e967673f79bb73f1fbc6f36d 100644 (file)
  */
 package org.openhab.binding.knx.internal.channel;
 
-import static java.util.stream.Collectors.toSet;
 import static org.openhab.binding.knx.internal.KNXBindingConstants.*;
 
 import java.util.Objects;
 import java.util.Set;
-import java.util.stream.Stream;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 
@@ -40,7 +38,7 @@ class TypeDimmer extends KNXChannelType {
 
     @Override
     protected Set<String> getAllGAKeys() {
-        return Stream.of(SWITCH_GA, POSITION_GA, INCREASE_DECREASE_GA).collect(toSet());
+        return Set.of(SWITCH_GA, POSITION_GA, INCREASE_DECREASE_GA);
     }
 
     @Override
index 27cd9bf11062eea84e07022c5743557f49aaba26..94a41563d01d7b592e931e2910ba6644e987dbc1 100644 (file)
  */
 package org.openhab.binding.knx.internal.channel;
 
-import static java.util.stream.Collectors.toSet;
 import static org.openhab.binding.knx.internal.KNXBindingConstants.*;
 
 import java.util.Objects;
 import java.util.Set;
-import java.util.stream.Stream;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 
@@ -53,6 +51,6 @@ class TypeRollershutter extends KNXChannelType {
 
     @Override
     protected Set<String> getAllGAKeys() {
-        return Stream.of(UP_DOWN_GA, STOP_MOVE_GA, POSITION_GA).collect(toSet());
+        return Set.of(UP_DOWN_GA, STOP_MOVE_GA, POSITION_GA);
     }
 }
index 7740173200af8ae31e339c8acd01554df0085467..2ef00e9aa713e33955e71e3b8d005c582c3ca9b6 100644 (file)
@@ -297,12 +297,11 @@ public abstract class AbstractKNXClient implements NetworkLinkListener, KNXClien
         }
     }
 
-    @SuppressWarnings("null")
     protected void releaseConnection() {
         logger.debug("Bridge {} is disconnecting from KNX bus", thingUID);
         var tmplink = link;
         if (tmplink != null) {
-            link.removeLinkListener(this);
+            tmplink.removeLinkListener(this);
         }
         busJob = nullify(busJob, j -> j.cancel(true));
         readDatapoints.clear();
@@ -321,7 +320,7 @@ public abstract class AbstractKNXClient implements NetworkLinkListener, KNXClien
         logger.trace("Bridge {} disconnected from KNX bus", thingUID);
     }
 
-    private <T> @Nullable T nullify(T target, @Nullable Consumer<T> lastWill) {
+    private <T> @Nullable T nullify(@Nullable T target, @Nullable Consumer<T> lastWill) {
         if (target != null && lastWill != null) {
             lastWill.accept(target);
         }
index db902650a10a98c8c794893fd45d1e9e04904d5b..5b2766f80686efa1c2d677447062dd6b685f104a 100644 (file)
@@ -977,7 +977,7 @@ public class KNXCoreTypeMapper implements KNXTypeMapper {
             if (typeClass.equals(DateTimeType.class)) {
                 String date = formatDateTime(value, datapoint.getDPT());
                 if (date.isEmpty()) {
-                    logger.debug("toType: KNX clock msg ignored: date object null or empty {}.", date);
+                    logger.debug("toType: KNX clock msg ignored: date object empty {}.", date);
                     return null;
                 } else {
                     return DateTimeType.valueOf(date);
@@ -1047,7 +1047,7 @@ public class KNXCoreTypeMapper implements KNXTypeMapper {
      * @return a formatted String like </code>yyyy-MM-dd'T'HH:mm:ss</code> which
      *         is target format of the {@link DateTimeType}
      */
-    private String formatDateTime(String value, String dpt) {
+    private String formatDateTime(String value, @Nullable String dpt) {
         Date date = null;
 
         try {
index 460d2362067106b8c9b20fd5c8ed414086deacfc..f07593596412f614a417c02ba4a94ab8222fe6ea 100644 (file)
@@ -14,8 +14,8 @@ package org.openhab.binding.knx.internal.factory;
 
 import static org.openhab.binding.knx.internal.KNXBindingConstants.*;
 
-import java.util.Arrays;
 import java.util.Collection;
+import java.util.Set;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
@@ -50,7 +50,7 @@ import org.osgi.service.component.annotations.Reference;
 @Component(service = ThingHandlerFactory.class, configurationPid = "binding.knx")
 public class KNXHandlerFactory extends BaseThingHandlerFactory {
 
-    public static final Collection<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Arrays.asList(THING_TYPE_DEVICE,
+    public static final Collection<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_DEVICE,
             THING_TYPE_IP_BRIDGE, THING_TYPE_SERIAL_BRIDGE);
 
     @Nullable
@@ -58,9 +58,11 @@ public class KNXHandlerFactory extends BaseThingHandlerFactory {
     private final SerialPortManager serialPortManager;
 
     @Activate
-    public KNXHandlerFactory(final @Reference TranslationProvider translationProvider,
-            final @Reference LocaleProvider localeProvider, final @Reference SerialPortManager serialPortManager) {
+    public KNXHandlerFactory(final @Reference NetworkAddressService networkAddressService,
+            final @Reference TranslationProvider translationProvider, final @Reference LocaleProvider localeProvider,
+            final @Reference SerialPortManager serialPortManager) {
         KNXTranslationProvider.I18N.setProvider(localeProvider, translationProvider);
+        this.networkAddressService = networkAddressService;
         this.serialPortManager = serialPortManager;
         SerialTransportAdapter.setSerialPortManager(serialPortManager);
     }
@@ -84,7 +86,7 @@ public class KNXHandlerFactory extends BaseThingHandlerFactory {
         if (THING_TYPE_DEVICE.equals(thingTypeUID)) {
             return super.createThing(thingTypeUID, configuration, thingUID, bridgeUID);
         }
-        throw new IllegalArgumentException("The thing type " + thingTypeUID + " is not supported by the KNX binding.");
+        return null;
     }
 
     @Override
@@ -116,13 +118,4 @@ public class KNXHandlerFactory extends BaseThingHandlerFactory {
         String serialPort = (String) configuration.get(SERIAL_PORT);
         return new ThingUID(thingTypeUID, serialPort);
     }
-
-    @Reference
-    protected void setNetworkAddressService(NetworkAddressService networkAddressService) {
-        this.networkAddressService = networkAddressService;
-    }
-
-    protected void unsetNetworkAddressService(NetworkAddressService networkAddressService) {
-        this.networkAddressService = null;
-    }
 }
index 397399f59eb65a5c5f7e2746a49addf677551449..fd181d44c47a6bab583cb480dec26e1109d2a342 100644 (file)
@@ -52,7 +52,7 @@ public abstract class AbstractKNXThingHandler extends BaseThingHandler implement
     private final Logger logger = LoggerFactory.getLogger(AbstractKNXThingHandler.class);
 
     protected @Nullable IndividualAddress address;
-    private @Nullable Future<?> descriptionJob;
+    private @Nullable ScheduledFuture<?> descriptionJob;
     private boolean filledDescription = false;
     private final Random random = new Random();
 
index 68d5c74b72e4f21bcaaa5ae9c1bb833778878353..aee86534ab45ac5ac1af239f89fc9fd5b6de0691 100644 (file)
@@ -15,13 +15,12 @@ package org.openhab.binding.knx.internal.handler;
 import static org.openhab.binding.knx.internal.KNXBindingConstants.*;
 
 import java.math.BigDecimal;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 
@@ -69,11 +68,11 @@ public class DeviceThingHandler extends AbstractKNXThingHandler {
     private final Logger logger = LoggerFactory.getLogger(DeviceThingHandler.class);
 
     private final KNXTypeMapper typeHelper = new KNXCoreTypeMapper();
-    private final Set<GroupAddress> groupAddresses = new HashSet<>();
-    private final Set<GroupAddress> groupAddressesWriteBlockedOnce = new HashSet<>();
-    private final Set<OutboundSpec> groupAddressesRespondingSpec = new HashSet<>();
-    private final Map<GroupAddress, ScheduledFuture<?>> readFutures = new HashMap<>();
-    private final Map<ChannelUID, ScheduledFuture<?>> channelFutures = new HashMap<>();
+    private final Set<GroupAddress> groupAddresses = ConcurrentHashMap.newKeySet();
+    private final Set<GroupAddress> groupAddressesWriteBlockedOnce = ConcurrentHashMap.newKeySet();
+    private final Set<OutboundSpec> groupAddressesRespondingSpec = ConcurrentHashMap.newKeySet();
+    private final Map<GroupAddress, ScheduledFuture<?>> readFutures = new ConcurrentHashMap<>();
+    private final Map<ChannelUID, ScheduledFuture<?>> channelFutures = new ConcurrentHashMap<>();
     private int readInterval;
 
     public DeviceThingHandler(Thing thing) {
@@ -99,20 +98,20 @@ public class DeviceThingHandler extends AbstractKNXThingHandler {
     @Override
     public void dispose() {
         cancelChannelFutures();
-        freeGroupAdresses();
+        freeGroupAddresses();
         super.dispose();
     }
 
     private void cancelChannelFutures() {
-        for (ScheduledFuture<?> future : channelFutures.values()) {
-            if (!future.isDone()) {
-                future.cancel(true);
-            }
+        for (ChannelUID channelUID : channelFutures.keySet()) {
+            channelFutures.computeIfPresent(channelUID, (k, v) -> {
+                v.cancel(true);
+                return null;
+            });
         }
-        channelFutures.clear();
     }
 
-    private void freeGroupAdresses() {
+    private void freeGroupAddresses() {
         groupAddresses.clear();
         groupAddressesWriteBlockedOnce.clear();
         groupAddressesRespondingSpec.clear();
@@ -120,12 +119,12 @@ public class DeviceThingHandler extends AbstractKNXThingHandler {
 
     @Override
     protected void cancelReadFutures() {
-        for (ScheduledFuture<?> future : readFutures.values()) {
-            if (!future.isDone()) {
-                future.cancel(true);
-            }
+        for (GroupAddress groupAddress : readFutures.keySet()) {
+            readFutures.computeIfPresent(groupAddress, (k, v) -> {
+                v.cancel(true);
+                return null;
+            });
         }
-        readFutures.clear();
     }
 
     @FunctionalInterface
@@ -220,7 +219,9 @@ public class DeviceThingHandler extends AbstractKNXThingHandler {
     @SuppressWarnings("null")
     private void rememberRespondingSpec(OutboundSpec commandSpec, boolean add) {
         GroupAddress ga = commandSpec.getGroupAddress();
-        groupAddressesRespondingSpec.removeIf(spec -> spec.getGroupAddress().equals(ga));
+        if (ga != null) {
+            groupAddressesRespondingSpec.removeIf(spec -> spec.getGroupAddress().equals(ga));
+        }
         if (add) {
             groupAddressesRespondingSpec.add(commandSpec);
         }
@@ -393,18 +394,18 @@ public class DeviceThingHandler extends AbstractKNXThingHandler {
                         && (type instanceof UnDefType || type instanceof IncreaseDecreaseType) && frequency > 0) {
                     // continuous dimming by the binding
                     if (UnDefType.UNDEF.equals(type)) {
-                        ScheduledFuture<?> future = channelFutures.remove(channelUID);
-                        if (future != null) {
-                            future.cancel(false);
-                        }
+                        channelFutures.computeIfPresent(channelUID, (k, v) -> {
+                            v.cancel(false);
+                            return null;
+                        });
                     } else if (type instanceof IncreaseDecreaseType) {
-                        ScheduledFuture<?> future = scheduler.scheduleWithFixedDelay(() -> {
-                            postCommand(channelUID, (Command) type);
-                        }, 0, frequency, TimeUnit.MILLISECONDS);
-                        ScheduledFuture<?> previousFuture = channelFutures.put(channelUID, future);
-                        if (previousFuture != null) {
-                            previousFuture.cancel(true);
-                        }
+                        channelFutures.compute(channelUID, (k, v) -> {
+                            if (v != null) {
+                                v.cancel(true);
+                            }
+                            return scheduler.scheduleWithFixedDelay(() -> postCommand(channelUID, (Command) type), 0,
+                                    frequency, TimeUnit.MILLISECONDS);
+                        });
                     }
                 } else {
                     if (type instanceof Command) {
index f5aa5d6ecc18c2c3c3f16ed01547bd6a63106cfc..e2067d7f3c7653288b67d11d78311f2215134c2a 100644 (file)
@@ -175,8 +175,9 @@ public class IPBridgeThingHandler extends KNXBridgeBaseThingHandler {
                 logger.debug("NetworkAddressService not available, cannot create bridge {}", thing.getUID());
                 updateStatus(ThingStatus.OFFLINE);
                 return;
+            } else {
+                localEndPoint = new InetSocketAddress(networkAddressService.getPrimaryIpv4HostAddress(), 0);
             }
-            localEndPoint = new InetSocketAddress(networkAddressService.getPrimaryIpv4HostAddress(), 0);
         }
 
         updateStatus(ThingStatus.UNKNOWN);
index eaa33e041cf63821c17277e90908e24a491429ec..14bbbb1b7910dec49530f94ff0b6ba5d3cfe8125 100644 (file)
@@ -36,11 +36,15 @@ class KNXChannelTypeTest {
         ct = new MyKNXChannelType("");
     }
 
-    @SuppressWarnings("null")
     @Test
     void testParseWithDptMultipleWithRead() {
         ChannelConfiguration res = ct.parse("5.001:<1/3/22+0/3/22+<0/8/15");
 
+        if (res == null) {
+            fail();
+            return;
+        }
+
         assertEquals("5.001", res.getDPT());
         assertEquals("1/3/22", res.getMainGA().getGA());
         assertTrue(res.getMainGA().isRead());
@@ -48,11 +52,15 @@ class KNXChannelTypeTest {
         assertEquals(2, res.getReadGAs().size());
     }
 
-    @SuppressWarnings("null")
     @Test
     void testParseWithDptMultipleWithoutRead() {
         ChannelConfiguration res = ct.parse("5.001:1/3/22+0/3/22+0/8/15");
 
+        if (res == null) {
+            fail();
+            return;
+        }
+
         assertEquals("5.001", res.getDPT());
         assertEquals("1/3/22", res.getMainGA().getGA());
         assertFalse(res.getMainGA().isRead());
@@ -60,11 +68,15 @@ class KNXChannelTypeTest {
         assertEquals(0, res.getReadGAs().size());
     }
 
-    @SuppressWarnings("null")
     @Test
     void testParseWithoutDptSingleWithoutRead() {
         ChannelConfiguration res = ct.parse("1/3/22");
 
+        if (res == null) {
+            fail();
+            return;
+        }
+
         assertNull(res.getDPT());
         assertEquals("1/3/22", res.getMainGA().getGA());
         assertFalse(res.getMainGA().isRead());
@@ -72,11 +84,15 @@ class KNXChannelTypeTest {
         assertEquals(0, res.getReadGAs().size());
     }
 
-    @SuppressWarnings("null")
     @Test
     void testParseWithoutDptSingleWitRead() {
         ChannelConfiguration res = ct.parse("<1/3/22");
 
+        if (res == null) {
+            fail();
+            return;
+        }
+
         assertNull(res.getDPT());
         assertEquals("1/3/22", res.getMainGA().getGA());
         assertTrue(res.getMainGA().isRead());
@@ -84,19 +100,29 @@ class KNXChannelTypeTest {
         assertEquals(1, res.getReadGAs().size());
     }
 
-    @SuppressWarnings("null")
     @Test
     void testParseTwoLevel() {
         ChannelConfiguration res = ct.parse("5.001:<3/1024+<4/1025");
+
+        if (res == null) {
+            fail();
+            return;
+        }
+
         assertEquals("3/1024", res.getMainGA().getGA());
         assertEquals(2, res.getListenGAs().size());
         assertEquals(2, res.getReadGAs().size());
     }
 
-    @SuppressWarnings("null")
     @Test
     void testParseFreeLevel() {
         ChannelConfiguration res = ct.parse("5.001:<4610+<4611");
+
+        if (res == null) {
+            fail();
+            return;
+        }
+
         assertEquals("4610", res.getMainGA().getGA());
         assertEquals(2, res.getListenGAs().size());
         assertEquals(2, res.getReadGAs().size());