]> git.basschouten.com Git - openhab-addons.git/commitdiff
[bigassfan] Null annotations (#13903)
authorlsiepel <leosiepel@gmail.com>
Thu, 5 Jan 2023 22:08:07 +0000 (23:08 +0100)
committerGitHub <noreply@github.com>
Thu, 5 Jan 2023 22:08:07 +0000 (23:08 +0100)
* Null annotations and some refactoring
* Fix synchronized block
* Fix remaining warnings

Signed-off-by: Leo Siepel <leosiepel@gmail.com>
bundles/org.openhab.binding.bigassfan/src/main/java/org/openhab/binding/bigassfan/internal/BigAssFanConfig.java
bundles/org.openhab.binding.bigassfan/src/main/java/org/openhab/binding/bigassfan/internal/discovery/BigAssFanDevice.java
bundles/org.openhab.binding.bigassfan/src/main/java/org/openhab/binding/bigassfan/internal/discovery/BigAssFanDiscoveryService.java
bundles/org.openhab.binding.bigassfan/src/main/java/org/openhab/binding/bigassfan/internal/discovery/DiscoveryListener.java
bundles/org.openhab.binding.bigassfan/src/main/java/org/openhab/binding/bigassfan/internal/handler/BigAssFanHandler.java
bundles/org.openhab.binding.bigassfan/src/main/java/org/openhab/binding/bigassfan/internal/utils/BigAssFanConverter.java

index b4ca71c4d816b9ae6f8d0b5301319b31db40cfa9..0948c5a854a30281d6cc08e3c0d6c1ed592796ab 100644 (file)
  */
 package org.openhab.binding.bigassfan.internal;
 
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
 /**
  * The {@link BigAssFanConfig} is responsible for storing the BigAssFan thing configuration.
  *
  * @author Mark Hilbush - Initial contribution
  */
+@NonNullByDefault
 public class BigAssFanConfig {
     /**
      * Name of the device
      */
-    private String label;
+    private String label = "";
 
     /**
      * IP address of the device
      */
-    private String ipAddress;
+    private String ipAddress = "";
 
     /**
      * MAC address of the device
      */
-    private String macAddress;
+    private String macAddress = "";
 
     public String getLabel() {
         return label;
@@ -58,16 +61,7 @@ public class BigAssFanConfig {
     }
 
     public boolean isValid() {
-        if (label == null || label.isBlank()) {
-            return false;
-        }
-        if (ipAddress == null || ipAddress.isBlank()) {
-            return false;
-        }
-        if (macAddress == null || macAddress.isBlank()) {
-            return false;
-        }
-        return true;
+        return !label.isBlank() && !ipAddress.isBlank() && !macAddress.isBlank();
     }
 
     @Override
index c2a0718f3f3b32ebf051aff2018cd418649c8edd..c477ab8830681a5f670e8df2bb1de6899c64b846 100644 (file)
  */
 package org.openhab.binding.bigassfan.internal.discovery;
 
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
 /**
  * The {@link BigAssFanDevice} is responsible for storing information about a fan.
  *
  * @author Mark Hilbush - Initial contribution
  */
+@NonNullByDefault
 public class BigAssFanDevice {
     /**
      * Name of device (e.g. Master Bedroom Fan)
      */
-    private String label;
+    private String label = "";
 
     /**
      * IP address of the device extracted from UDP packet
      */
-    private String ipAddress;
+    private String ipAddress = "";
 
     /**
      * MAC address of the device extracted from discovery message
      */
-    private String macAddress;
+    private String macAddress = "";
 
     /**
      * Type of device extracted from discovery message (e.g. FAN or SWITCH)
      */
-    private String type;
+    private String type = "";
 
     /**
      * Model of device extracted from discovery message (e.g. HSERIES)
      */
-    private String model;
+    private String model = "";
 
     /**
      * The raw discovery message
      */
-    private String discoveryMessage;
+    private String discoveryMessage = "";
 
     public String getLabel() {
         return label;
index 15f17ac0b31096b4a499078372583104ff8f2e67..d311c4b31d47c4937a6af27c8dc246abed8d38b9 100644 (file)
@@ -27,6 +27,8 @@ import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.core.config.discovery.AbstractDiscoveryService;
 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
 import org.openhab.core.config.discovery.DiscoveryService;
@@ -44,6 +46,7 @@ import org.slf4j.LoggerFactory;
  *
  * @author Mark Hilbush - Initial contribution
  */
+@NonNullByDefault
 @Component(service = DiscoveryService.class, configurationPid = "discovery.bigassfan")
 public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
     private final Logger logger = LoggerFactory.getLogger(BigAssFanDiscoveryService.class);
@@ -53,12 +56,9 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
 
     // Our own thread pool for the long-running listener job
     private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
-    private ScheduledFuture<?> listenerJob;
-
-    DiscoveryListener discoveryListener;
-
+    private @Nullable ScheduledFuture<?> listenerJob;
+    private @Nullable DiscoveryListener discoveryListener;
     private boolean terminate;
-
     private final Pattern announcementPattern = Pattern.compile("[(](.*);DEVICE;ID;(.*);(.*)[)]");
 
     private Runnable listenerRunnable = () -> {
@@ -70,9 +70,9 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
     };
 
     // Frequency (in seconds) with which we poll for new devices
-    private final long POLL_FREQ = 300L;
-    private final long POLL_DELAY = 12L;
-    private ScheduledFuture<?> pollJob;
+    private static final long POLL_FREQ = 300L;
+    private static final long POLL_DELAY = 12L;
+    private @Nullable ScheduledFuture<?> pollJob;
 
     public BigAssFanDiscoveryService() {
         super(SUPPORTED_THING_TYPES_UIDS, 0, BACKGROUND_DISCOVERY_ENABLED);
@@ -84,7 +84,7 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
     }
 
     @Override
-    protected void activate(Map<String, Object> configProperties) {
+    protected void activate(@Nullable Map<String, Object> configProperties) {
         super.activate(configProperties);
         logger.trace("BigAssFan discovery service ACTIVATED");
     }
@@ -97,7 +97,7 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
 
     @Override
     @Modified
-    protected void modified(Map<String, Object> configProperties) {
+    protected void modified(@Nullable Map<String, Object> configProperties) {
         super.modified(configProperties);
     }
 
@@ -115,21 +115,22 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
         cancelListenerJob();
     }
 
-    private void startListenerJob() {
-        if (listenerJob == null) {
-            terminate = false;
+    private synchronized void startListenerJob() {
+        if (this.listenerJob == null) {
             logger.debug("Starting discovery listener job in {} seconds", BACKGROUND_DISCOVERY_DELAY);
-            listenerJob = scheduledExecutorService.schedule(listenerRunnable, BACKGROUND_DISCOVERY_DELAY,
+            terminate = false;
+            this.listenerJob = scheduledExecutorService.schedule(listenerRunnable, BACKGROUND_DISCOVERY_DELAY,
                     TimeUnit.SECONDS);
         }
     }
 
     private void cancelListenerJob() {
-        if (listenerJob != null) {
+        ScheduledFuture<?> localListenerJob = this.listenerJob;
+        if (localListenerJob != null) {
             logger.debug("Canceling discovery listener job");
-            listenerJob.cancel(true);
+            localListenerJob.cancel(true);
             terminate = true;
-            listenerJob = null;
+            this.listenerJob = null;
         }
     }
 
@@ -143,9 +144,11 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
 
     private synchronized void listen() {
         logger.info("BigAssFan discovery service is running");
+        DiscoveryListener localDiscoveryListener;
 
         try {
-            discoveryListener = new DiscoveryListener();
+            localDiscoveryListener = new DiscoveryListener();
+            discoveryListener = localDiscoveryListener;
         } catch (SocketException se) {
             logger.warn("Got Socket exception creating multicast socket: {}", se.getMessage(), se);
             return;
@@ -158,7 +161,7 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
         while (!terminate) {
             try {
                 // Wait for a discovery message
-                processMessage(discoveryListener.waitForMessage());
+                processMessage(localDiscoveryListener.waitForMessage());
             } catch (SocketTimeoutException e) {
                 // Read on socket timed out; check for termination
                 continue;
@@ -167,14 +170,11 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
                 break;
             }
         }
-        discoveryListener.shutdown();
+        localDiscoveryListener.shutdown();
         logger.debug("DiscoveryListener job is exiting");
     }
 
     private void processMessage(BigAssFanDevice device) {
-        if (device == null) {
-            return;
-        }
         Matcher matcher = announcementPattern.matcher(device.getDiscoveryMessage());
         if (matcher.find()) {
             logger.debug("Match: grp1={}, grp2={}, grp(3)={}", matcher.group(1), matcher.group(2), matcher.group(3));
@@ -242,23 +242,30 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
                 .withRepresentationProperty(THING_PROPERTY_MAC).withLabel(device.getLabel()).build());
     }
 
-    private void schedulePollJob() {
-        logger.debug("Scheduling discovery poll job to run every {} seconds starting in {} sec", POLL_FREQ, POLL_DELAY);
+    private synchronized void schedulePollJob() {
         cancelPollJob();
-        pollJob = scheduler.scheduleWithFixedDelay(() -> {
-            try {
-                discoveryListener.pollForDevices();
-            } catch (RuntimeException e) {
-                logger.warn("Poll job got unexpected exception: {}", e.getMessage(), e);
-            }
-        }, POLL_DELAY, POLL_FREQ, TimeUnit.SECONDS);
+        if (this.pollJob == null) {
+            logger.debug("Scheduling discovery poll job to run every {} seconds starting in {} sec", POLL_FREQ,
+                    POLL_DELAY);
+            pollJob = scheduler.scheduleWithFixedDelay(() -> {
+                try {
+                    DiscoveryListener localListener = discoveryListener;
+                    if (localListener != null) {
+                        localListener.pollForDevices();
+                    }
+                } catch (RuntimeException e) {
+                    logger.warn("Poll job got unexpected exception: {}", e.getMessage(), e);
+                }
+            }, POLL_DELAY, POLL_FREQ, TimeUnit.SECONDS);
+        }
     }
 
     private void cancelPollJob() {
-        if (pollJob != null) {
+        ScheduledFuture<?> localPollJob = pollJob;
+        if (localPollJob != null) {
             logger.debug("Canceling poll job");
-            pollJob.cancel(true);
-            pollJob = null;
+            localPollJob.cancel(true);
+            this.pollJob = null;
         }
     }
 }
index c8c5883b5c0adb36b66e1cb192105fd6cdda9943..c990a6c061782043302f30162826bc7faca85249 100644 (file)
@@ -23,6 +23,8 @@ import java.net.SocketTimeoutException;
 import java.net.UnknownHostException;
 import java.nio.charset.StandardCharsets;
 
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -31,19 +33,23 @@ import org.slf4j.LoggerFactory;
  *
  * @author Mark Hilbush - Initial contribution
  */
+@NonNullByDefault
 public class DiscoveryListener {
     private final Logger logger = LoggerFactory.getLogger(DiscoveryListener.class);
 
-    private final String BCAST_ADDRESS = "255.255.255.255";
-    private final int SOCKET_RECEIVE_TIMEOUT = 500;
-
-    private final String POLL_MESSAGE = "<ALL;DEVICE;ID;GET>";
+    private static final String BCAST_ADDRESS = "255.255.255.255";
+    private static final int SOCKET_RECEIVE_TIMEOUT = 500;
+    private static final String POLL_MESSAGE = "<ALL;DEVICE;ID;GET>";
 
+    @Nullable
     DatagramSocket dSocket;
+    @Nullable
     DatagramPacket rcvPacket;
-    byte[] rcvBuffer;
+    byte[] rcvBuffer = new byte[0];
+    @Nullable
     InetAddress bcastAddress;
-    byte[] bcastBuffer;
+    byte[] bcastBuffer = new byte[0];
+    @Nullable
     DatagramPacket bcastPacket;
 
     BigAssFanDevice device;
@@ -54,52 +60,66 @@ public class DiscoveryListener {
         device = new BigAssFanDevice();
         try {
             // Create a socket on the UDP port and get send & receive buffers
-            dSocket = new DatagramSocket(BAF_PORT);
-            dSocket.setSoTimeout(SOCKET_RECEIVE_TIMEOUT);
-            dSocket.setBroadcast(true);
+            DatagramSocket localDatagramSocket = new DatagramSocket(BAF_PORT);
+            localDatagramSocket.setSoTimeout(SOCKET_RECEIVE_TIMEOUT);
+            localDatagramSocket.setBroadcast(true);
+            dSocket = localDatagramSocket;
             rcvBuffer = new byte[256];
             rcvPacket = new DatagramPacket(rcvBuffer, rcvBuffer.length);
             bcastAddress = InetAddress.getByName(BCAST_ADDRESS);
             bcastBuffer = POLL_MESSAGE.getBytes(StandardCharsets.US_ASCII);
             bcastPacket = new DatagramPacket(bcastBuffer, bcastBuffer.length, bcastAddress, BAF_PORT);
-        } catch (UnknownHostException uhe) {
-            logger.warn("UnknownHostException sending poll request for fans: {}", uhe.getMessage(), uhe);
+        } catch (UnknownHostException | SocketException | SecurityException e) {
+            logger.warn("Unexpected exception sending poll request for fans: {}", e.getMessage(), e);
         }
     }
 
     public BigAssFanDevice waitForMessage() throws IOException, SocketTimeoutException {
         // Wait to receive a packet
-        rcvPacket.setLength(rcvBuffer.length);
-        dSocket.receive(rcvPacket);
+        DatagramPacket localPacket = rcvPacket;
+        DatagramSocket localDatagramSocket = dSocket;
+
+        if (localPacket != null) {
+            localPacket.setLength(rcvBuffer.length);
+        }
+
+        if (localDatagramSocket != null && localPacket != null) {
+            localDatagramSocket.receive(localPacket);
 
-        // Process the received packet
-        device.reset();
-        device.setIpAddress(rcvPacket.getAddress().getHostAddress());
-        String message = (new String(rcvBuffer, 0, rcvPacket.getLength()));
-        device.setDiscoveryMessage(message);
-        logger.debug("RECEIVED packet of length {} from {}: {}", message.length(), device.getIpAddress(), message);
+            // Process the received packet
+            device.reset();
+
+            String address = localPacket.getAddress().getHostAddress();
+            device.setIpAddress(address != null ? address : "");
+
+            String message = (new String(rcvBuffer, 0, localPacket.getLength()));
+            device.setDiscoveryMessage(message);
+            logger.debug("RECEIVED packet of length {} from {}: {}", message.length(), device.getIpAddress(), message);
+        }
 
         return device;
     }
 
     public void pollForDevices() {
-        if (dSocket == null) {
+        DatagramSocket localDatagramSocket = dSocket;
+        if (localDatagramSocket == null) {
             logger.debug("Socket is null in discoveryListener.pollForDevices()");
             return;
         }
 
         logger.debug("Sending poll request for fans: {}", POLL_MESSAGE);
         try {
-            dSocket.send(bcastPacket);
-        } catch (IOException ioe) {
-            logger.warn("IOException sending poll request for fans: {}", ioe.getMessage(), ioe);
+            localDatagramSocket.send(bcastPacket);
+        } catch (IllegalArgumentException | SecurityException | IOException e) {
+            logger.warn("Unexpected exception while sending poll request for fans: {}", e.getMessage(), e);
         }
     }
 
     public void shutdown() {
         logger.debug("DiscoveryListener closing socket");
-        if (dSocket != null) {
-            dSocket.close();
+        DatagramSocket localDatagramSocket = dSocket;
+        if (localDatagramSocket != null) {
+            localDatagramSocket.close();
             dSocket = null;
         }
     }
index 31bafe70639b0426ae6ec47d41e9fe80b18d5d3b..dbb6485f28d532c2977801d56749418281ea6881 100644 (file)
@@ -23,6 +23,7 @@ import java.net.Socket;
 import java.net.SocketException;
 import java.net.UnknownHostException;
 import java.nio.BufferOverflowException;
+import java.nio.channels.IllegalBlockingModeException;
 import java.nio.charset.StandardCharsets;
 import java.time.Instant;
 import java.time.ZoneId;
@@ -40,6 +41,8 @@ import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.bigassfan.internal.BigAssFanConfig;
 import org.openhab.binding.bigassfan.internal.utils.BigAssFanConverter;
 import org.openhab.core.common.ThreadPoolManager;
@@ -65,6 +68,7 @@ import org.slf4j.LoggerFactory;
  *
  * @author Mark Hilbush - Initial contribution
  */
+@NonNullByDefault
 public class BigAssFanHandler extends BaseThingHandler {
     private final Logger logger = LoggerFactory.getLogger(BigAssFanHandler.class);
 
@@ -75,16 +79,15 @@ public class BigAssFanHandler extends BaseThingHandler {
     private static final StringType COOLING = new StringType("COOLING");
     private static final StringType HEATING = new StringType("HEATING");
 
-    private BigAssFanConfig config;
-    private String label = null;
-    private String ipAddress = null;
-    private String macAddress = null;
+    private String label = "";
+    private String ipAddress = "";
+    private String macAddress = "";
 
-    private FanListener fanListener;
+    private final FanListener fanListener;
 
-    protected Map<String, State> fanStateMap = Collections.synchronizedMap(new HashMap<>());
+    protected final Map<String, State> fanStateMap = Collections.synchronizedMap(new HashMap<>());
 
-    public BigAssFanHandler(Thing thing, String ipv4Address) {
+    public BigAssFanHandler(Thing thing, @Nullable String ipv4Address) {
         super(thing);
         this.thing = thing;
 
@@ -96,18 +99,19 @@ public class BigAssFanHandler extends BaseThingHandler {
     public void initialize() {
         logger.debug("BigAssFanHandler for {} is initializing", thing.getUID());
 
-        config = getConfig().as(BigAssFanConfig.class);
-        logger.debug("BigAssFanHandler config for {} is {}", thing.getUID(), config);
+        BigAssFanConfig configuration = getConfig().as(BigAssFanConfig.class);
+        logger.debug("BigAssFanHandler config for {} is {}", thing.getUID(), configuration);
 
-        if (!config.isValid()) {
+        if (!configuration.isValid()) {
             logger.debug("BigAssFanHandler config of {} is invalid. Check configuration", thing.getUID());
             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
                     "Invalid BigAssFan config. Check configuration.");
             return;
         }
-        label = config.getLabel();
-        ipAddress = config.getIpAddress();
-        macAddress = config.getMacAddress();
+
+        label = configuration.getLabel();
+        ipAddress = configuration.getIpAddress();
+        macAddress = configuration.getMacAddress();
 
         fanListener.startFanListener();
     }
@@ -312,8 +316,9 @@ public class BigAssFanHandler extends BaseThingHandler {
     private void adjustMaxSpeed(PercentType command, String channelId, String commandFragment) {
         int newMin = command.intValue();
         int currentMax = PercentType.ZERO.intValue();
-        if (fanStateMap.get(channelId) != null) {
-            currentMax = ((PercentType) fanStateMap.get(channelId)).intValue();
+        State fanState = fanStateMap.get(channelId);
+        if (fanState != null) {
+            currentMax = ((PercentType) fanState).intValue();
         }
         if (newMin > currentMax) {
             updateState(CHANNEL_FAN_SPEED_MAX, command);
@@ -324,8 +329,9 @@ public class BigAssFanHandler extends BaseThingHandler {
     private void adjustMinSpeed(PercentType command, String channelId, String commandFragment) {
         int newMax = command.intValue();
         int currentMin = PercentType.HUNDRED.intValue();
-        if (fanStateMap.get(channelId) != null) {
-            currentMin = ((PercentType) fanStateMap.get(channelId)).intValue();
+        State fanSate = fanStateMap.get(channelId);
+        if (fanSate != null) {
+            currentMin = ((PercentType) fanSate).intValue();
         }
         if (newMax < currentMin) {
             updateState(channelId, command);
@@ -449,8 +455,9 @@ public class BigAssFanHandler extends BaseThingHandler {
     private void adjustMaxLevel(PercentType command) {
         int newMin = command.intValue();
         int currentMax = PercentType.ZERO.intValue();
-        if (fanStateMap.get(CHANNEL_LIGHT_LEVEL_MAX) != null) {
-            currentMax = ((PercentType) fanStateMap.get(CHANNEL_LIGHT_LEVEL_MAX)).intValue();
+        State fanState = fanStateMap.get(CHANNEL_LIGHT_LEVEL_MAX);
+        if (fanState != null) {
+            currentMax = ((PercentType) fanState).intValue();
         }
         if (newMin > currentMax) {
             updateState(CHANNEL_LIGHT_LEVEL_MAX, command);
@@ -461,8 +468,9 @@ public class BigAssFanHandler extends BaseThingHandler {
     private void adjustMinLevel(PercentType command) {
         int newMax = command.intValue();
         int currentMin = PercentType.HUNDRED.intValue();
-        if (fanStateMap.get(CHANNEL_LIGHT_LEVEL_MIN) != null) {
-            currentMin = ((PercentType) fanStateMap.get(CHANNEL_LIGHT_LEVEL_MIN)).intValue();
+        State fanState = fanStateMap.get(CHANNEL_LIGHT_LEVEL_MIN);
+        if (fanState != null) {
+            currentMin = ((PercentType) fanState).intValue();
         }
         if (newMax < currentMin) {
             updateState(CHANNEL_LIGHT_LEVEL_MIN, command);
@@ -483,11 +491,6 @@ public class BigAssFanHandler extends BaseThingHandler {
      * Send a command to the fan
      */
     private void sendCommand(String mac, String commandFragment) {
-        if (fanListener == null) {
-            logger.error("Unable to send message to {} because fanListener object is null!", thing.getUID());
-            return;
-        }
-
         StringBuilder sb = new StringBuilder();
         sb.append("<").append(mac).append(commandFragment).append(">");
         String message = sb.toString();
@@ -519,7 +522,7 @@ public class BigAssFanHandler extends BaseThingHandler {
         }
     }
 
-    private void markOfflineWithMessage(ThingStatusDetail statusDetail, String statusMessage) {
+    private void markOfflineWithMessage(ThingStatusDetail statusDetail, @Nullable String statusMessage) {
         // If it's offline with no detail or if it's not offline, mark it offline with detailed status
         if ((isOffline() && getDetail() == ThingStatusDetail.NONE) || !isOffline()) {
             logger.debug("Changing status of {} from {}({}) to OFFLINE({})", thing.getUID(), getStatus(), getDetail(),
@@ -556,9 +559,9 @@ public class BigAssFanHandler extends BaseThingHandler {
         // Our own thread pool for the long-running listener job
         private ScheduledExecutorService scheduledExecutorService = ThreadPoolManager
                 .getScheduledPool("bigassfanHandler" + "-" + thing.getUID());
-        private ScheduledFuture<?> listenerJob;
+        private @Nullable ScheduledFuture<?> listenerJob;
 
-        private final long FAN_LISTENER_DELAY = 2L;
+        private static final long FAN_LISTENER_DELAY = 2L;
         private boolean terminate;
 
         private final Pattern messagePattern = Pattern.compile("[(](.*)");
@@ -573,7 +576,7 @@ public class BigAssFanHandler extends BaseThingHandler {
             }
         };
 
-        public FanListener(String ipv4Address) {
+        public FanListener(@Nullable String ipv4Address) {
             conn = new ConnectionManager(ipv4Address);
         }
 
@@ -590,12 +593,14 @@ public class BigAssFanHandler extends BaseThingHandler {
         }
 
         public void stopFanListener() {
-            if (listenerJob != null) {
+            ScheduledFuture<?> localListenerJob = listenerJob;
+            if (localListenerJob != null) {
                 logger.debug("Stopping listener for {} at {}", thing.getUID(), ipAddress);
                 terminate = true;
-                listenerJob.cancel(true);
-                listenerJob = null;
+                localListenerJob.cancel(true);
+                this.listenerJob = null;
             }
+
             conn.cancelConnectionMonitorJob();
             conn.disconnect();
         }
@@ -636,7 +641,7 @@ public class BigAssFanHandler extends BaseThingHandler {
             logger.debug("Fan listener thread is exiting for {} at {}", thing.getUID(), ipAddress);
         }
 
-        private String waitForMessage() throws IOException {
+        private @Nullable String waitForMessage() throws IOException {
             if (!conn.isConnected()) {
                 if (logger.isTraceEnabled()) {
                     logger.trace("FanListener for {} can't receive message. No connection to fan", thing.getUID());
@@ -650,7 +655,7 @@ public class BigAssFanHandler extends BaseThingHandler {
             return readMessage();
         }
 
-        private String readMessage() {
+        private @Nullable String readMessage() {
             logger.trace("Waiting for message from {}  at {}", thing.getUID(), ipAddress);
             String message = conn.read();
             if (message != null) {
@@ -660,7 +665,7 @@ public class BigAssFanHandler extends BaseThingHandler {
             return message;
         }
 
-        private void processMessage(String incomingMessage) {
+        private void processMessage(@Nullable String incomingMessage) {
             if (incomingMessage == null || incomingMessage.isEmpty()) {
                 return;
             }
@@ -739,10 +744,7 @@ public class BigAssFanHandler extends BaseThingHandler {
                 return true;
             }
             // Didn't match MAC address, check match for label
-            if (label.equalsIgnoreCase(idFromDevice)) {
-                return true;
-            }
-            return false;
+            return label.equalsIgnoreCase(idFromDevice);
         }
 
         private void updateFanPower(String[] messageParts) {
@@ -1003,27 +1005,28 @@ public class BigAssFanHandler extends BaseThingHandler {
 
         private boolean deviceIsConnected;
 
-        private InetAddress ifAddress;
-        private Socket fanSocket;
-        private Scanner fanScanner;
-        private DataOutputStream fanWriter;
-        private final int SOCKET_CONNECT_TIMEOUT = 1500;
+        private @Nullable InetAddress ifAddress;
+        private @Nullable Socket fanSocket;
+        private @Nullable Scanner fanScanner;
+        private @Nullable DataOutputStream fanWriter;
+        private static final int SOCKET_CONNECT_TIMEOUT = 1500;
 
-        ScheduledFuture<?> connectionMonitorJob;
-        private final long CONNECTION_MONITOR_FREQ = 120L;
-        private final long CONNECTION_MONITOR_DELAY = 30L;
+        private @Nullable ScheduledFuture<?> connectionMonitorJob;
+        private static final long CONNECTION_MONITOR_FREQ = 120L;
+        private static final long CONNECTION_MONITOR_DELAY = 30L;
 
         Runnable connectionMonitorRunnable = () -> {
             logger.trace("Performing connection check for {} at IP {}", thing.getUID(), ipAddress);
             checkConnection();
         };
 
-        public ConnectionManager(String ipv4Address) {
+        public ConnectionManager(@Nullable String ipv4Address) {
             deviceIsConnected = false;
             try {
                 ifAddress = InetAddress.getByName(ipv4Address);
-                logger.debug("Handler for {} using address {} on network interface {}", thing.getUID(),
-                        ifAddress.getHostAddress(), NetworkInterface.getByInetAddress(ifAddress).getName());
+
+                logger.debug("Handler for {} using address {} on network interface {}", thing.getUID(), ipv4Address,
+                        NetworkInterface.getByInetAddress(ifAddress).getName());
             } catch (UnknownHostException e) {
                 logger.warn("Handler for {} got UnknownHostException getting local IPv4 net interface: {}",
                         thing.getUID(), e.getMessage(), e);
@@ -1045,13 +1048,15 @@ public class BigAssFanHandler extends BaseThingHandler {
             }
             logger.trace("Connecting to {} at {}", thing.getUID(), ipAddress);
 
+            Socket localFanSocket = new Socket();
+            fanSocket = localFanSocket;
             // Open socket
             try {
-                fanSocket = new Socket();
-                fanSocket.bind(new InetSocketAddress(ifAddress, 0));
-                fanSocket.connect(new InetSocketAddress(ipAddress, BAF_PORT), SOCKET_CONNECT_TIMEOUT);
-            } catch (IOException e) {
-                logger.debug("IOException connecting to  {} at {}: {}", thing.getUID(), ipAddress, e.getMessage());
+                localFanSocket.bind(new InetSocketAddress(ifAddress, 0));
+                localFanSocket.connect(new InetSocketAddress(ipAddress, BAF_PORT), SOCKET_CONNECT_TIMEOUT);
+            } catch (SecurityException | IllegalArgumentException | IOException e) {
+                logger.debug("Unexpected exception connecting to {} at {}: {}", thing.getUID(), ipAddress,
+                        e.getMessage(), e);
                 markOfflineWithMessage(ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
                 disconnect();
                 return;
@@ -1059,12 +1064,12 @@ public class BigAssFanHandler extends BaseThingHandler {
 
             // Create streams
             try {
-                fanWriter = new DataOutputStream(fanSocket.getOutputStream());
-                fanScanner = new Scanner(fanSocket.getInputStream());
-                fanScanner.useDelimiter("[)]");
-            } catch (IOException e) {
-                logger.warn("IOException getting streams for {} at {}: {}", thing.getUID(), ipAddress, e.getMessage(),
-                        e);
+                fanWriter = new DataOutputStream(localFanSocket.getOutputStream());
+                Scanner localFanScanner = new Scanner(localFanSocket.getInputStream());
+                localFanScanner.useDelimiter("[)]");
+                fanScanner = localFanScanner;
+            } catch (IllegalBlockingModeException | IOException e) {
+                logger.warn("Exception getting streams for {} at {}: {}", thing.getUID(), ipAddress, e.getMessage(), e);
                 markOfflineWithMessage(ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
                 disconnect();
                 return;
@@ -1081,17 +1086,22 @@ public class BigAssFanHandler extends BaseThingHandler {
             logger.debug("Disconnecting from {} at {}", thing.getUID(), ipAddress);
 
             try {
-                if (fanWriter != null) {
-                    fanWriter.close();
+                DataOutputStream localFanWriter = fanWriter;
+                if (localFanWriter != null) {
+                    localFanWriter.close();
+                    fanWriter = null;
                 }
-                if (fanScanner != null) {
-                    fanScanner.close();
+                Scanner localFanScanner = fanScanner;
+                if (localFanScanner != null) {
+                    localFanScanner.close();
                 }
-                if (fanSocket != null) {
-                    fanSocket.close();
+                Socket localFanSocket = fanSocket;
+                if (localFanSocket != null) {
+                    localFanSocket.close();
+                    fanSocket = null;
                 }
-            } catch (IOException e) {
-                logger.warn("IOException closing connection to {} at {}: {}", thing.getUID(), ipAddress, e.getMessage(),
+            } catch (IllegalStateException | IOException e) {
+                logger.warn("Exception closing connection to {} at {}: {}", thing.getUID(), ipAddress, e.getMessage(),
                         e);
             }
             deviceIsConnected = false;
@@ -1101,15 +1111,18 @@ public class BigAssFanHandler extends BaseThingHandler {
             markOffline();
         }
 
-        public String read() {
+        public @Nullable String read() {
             if (fanScanner == null) {
                 logger.warn("Scanner for {} is null when trying to scan from {}!", thing.getUID(), ipAddress);
                 return null;
             }
 
-            String nextToken;
+            String nextToken = null;
             try {
-                nextToken = fanScanner.next();
+                Scanner localFanScanner = fanScanner;
+                if (localFanScanner != null) {
+                    nextToken = localFanScanner.next();
+                }
             } catch (NoSuchElementException e) {
                 logger.debug("Scanner for {} threw NoSuchElementException; stream possibly closed", thing.getUID());
                 // Force a reconnect to the device
@@ -1126,11 +1139,13 @@ public class BigAssFanHandler extends BaseThingHandler {
         }
 
         public void write(byte[] buffer) throws IOException {
-            if (fanWriter == null) {
+            DataOutputStream localFanWriter = fanWriter;
+            if (localFanWriter == null) {
                 logger.warn("fanWriter for {} is null when trying to write to {}!!!", thing.getUID(), ipAddress);
                 return;
+            } else {
+                localFanWriter.write(buffer, 0, buffer.length);
             }
-            fanWriter.write(buffer, 0, buffer.length);
         }
 
         private boolean isConnected() {
@@ -1140,7 +1155,7 @@ public class BigAssFanHandler extends BaseThingHandler {
         /*
          * Periodically validate the command connection to the device by executing a getversion command.
          */
-        private void scheduleConnectionMonitorJob() {
+        private synchronized void scheduleConnectionMonitorJob() {
             if (connectionMonitorJob == null) {
                 logger.debug("Starting connection monitor job in {} seconds for {} at {}", CONNECTION_MONITOR_DELAY,
                         thing.getUID(), ipAddress);
@@ -1150,9 +1165,10 @@ public class BigAssFanHandler extends BaseThingHandler {
         }
 
         private void cancelConnectionMonitorJob() {
-            if (connectionMonitorJob != null) {
+            ScheduledFuture<?> localConnectionMonitorJob = connectionMonitorJob;
+            if (localConnectionMonitorJob != null) {
                 logger.debug("Canceling connection monitor job for {} at {}", thing.getUID(), ipAddress);
-                connectionMonitorJob.cancel(true);
+                localConnectionMonitorJob.cancel(true);
                 connectionMonitorJob = null;
             }
         }
index a79ab81c296af45feba14e8a6e2f8e85e532848c..2266ddae58f899695f1aa7bd2d5f8aaec8c48166 100644 (file)
@@ -12,6 +12,7 @@
  */
 package org.openhab.binding.bigassfan.internal.utils;
 
+import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.openhab.core.library.types.PercentType;
 
 /**
@@ -21,6 +22,7 @@ import org.openhab.core.library.types.PercentType;
  *
  * @author Mark Hilbush - Initial contribution
  */
+@NonNullByDefault
 public class BigAssFanConverter {
     /*
      * Conversion factor for fan range (0-7) to dimmer range (0-100).