gt:                                             if (theLock != null && theLock.isLocked()) {
context: /**
   * Copyright (c) 2010-2020 Contributors to the openHAB project
   *
   * See the NOTICE file(s) distributed with this work for additional
   * information.
   *
   * This program and the accompanying materials are made available under the
   * terms of the Eclipse Public License 2.0 which is available at
   * http://www.eclipse.org/legal/epl-2.0
   *
   * SPDX-License-Identifier: EPL-2.0
   */
  package org.openhab.binding.keba.internal.handler;
  
  import java.io.IOException;
  import java.io.UnsupportedEncodingException;
  import java.net.InetSocketAddress;
  import java.net.PortUnreachableException;
  import java.nio.ByteBuffer;
  import java.nio.channels.CancelledKeyException;
  import java.nio.channels.ClosedChannelException;
  import java.nio.channels.ClosedSelectorException;
  import java.nio.channels.DatagramChannel;
  import java.nio.channels.NotYetConnectedException;
  import java.nio.channels.SelectionKey;
  import java.nio.channels.Selector;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.Map;
  import java.util.Set;
  import java.util.concurrent.locks.ReentrantLock;
  
  import org.openhab.core.thing.ThingStatus;
  import org.openhab.core.thing.ThingStatusDetail;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * The {@link KeContactTransceiver} is responsible for receiving UDP broadcast messages sent by the KEBA Charging
   * Stations. {@link KeContactHandler} willing to receive these messages have to register themselves with the
   * {@link KeContactTransceiver}
   *
   * @author Karel Goderis - Initial contribution
   */
  
  class KeContactTransceiver {
  
      public static final int LISTENER_PORT_NUMBER = 7090;
      public static final int REMOTE_PORT_NUMBER = 7090;
      public static final int LISTENING_INTERVAL = 100;
      public static final int BUFFER_SIZE = 1024;
      public static final String IP_ADDRESS = "ipAddress";
      public static final String POLLING_REFRESH_INTERVAL = "refreshInterval";
  
      private DatagramChannel broadcastChannel;
      private SelectionKey broadcastKey;
      private Selector selector;
      private Thread transceiverThread;
      private boolean isStarted = false;
      private Set<KeContactHandler> handlers = Collections.synchronizedSet(new HashSet<>());
      private Map<KeContactHandler, DatagramChannel> datagramChannels = Collections.synchronizedMap(new HashMap<>());
      private Map<KeContactHandler, ByteBuffer> buffers = Collections.synchronizedMap(new HashMap<>());
      private Map<KeContactHandler, ReentrantLock> locks = Collections.synchronizedMap(new HashMap<>());
      private Map<KeContactHandler, Boolean> flags = Collections.synchronizedMap(new HashMap<>());
  
      private final Logger logger = LoggerFactory.getLogger(KeContactTransceiver.class);
  
      public void start() {
          if (!isStarted) {
              logger.debug("Starting the the KEBA KeContact transceiver");
              try {
                  selector = Selector.open();
  
                  if (transceiverThread == null) {
                      transceiverThread = new Thread(transceiverRunnable, "openHAB-Keba-Transceiver");
                      transceiverThread.start();
                  }
  
                  broadcastChannel = DatagramChannel.open();
                  broadcastChannel.socket().bind(new InetSocketAddress(LISTENER_PORT_NUMBER));
                  broadcastChannel.configureBlocking(false);
  
                  logger.info("Listening for incoming data on {}", broadcastChannel.getLocalAddress());
  
                  synchronized (selector) {
                      selector.wakeup();
                      broadcastKey = broadcastChannel.register(selector, broadcastChannel.validOps());
                  }
  
                  for (KeContactHandler listener : handlers) {
                      establishConnection(listener);
                  }
  
                  isStarted = true;
              } catch (ClosedSelectorException | CancelledKeyException | IOException e) {
                  logger.error("An exception occurred while registering the selector: {}", e.getMessage());
              }
          }
      }
  
      public void stop() {
          if (isStarted) {
              for (KeContactHandler listener : handlers) {
                  this.removeConnection(listener);
              }
  
              try {
                  broadcastChannel.close();
              } catch (IOException e) {
                  logger.error("An exception occurred while closing the broadcast channel on port number {} : '{}'",
                          LISTENER_PORT_NUMBER, e.getMessage(), e);
              }
  
              try {
                  selector.close();
              } catch (IOException e) {
                  logger.error("An exception occurred while closing the selector: '{}'", e.getMessage(), e);
              }
  
              logger.debug("Stopping the the KEBA KeContact transceiver");
              if (transceiverThread != null) {
                  transceiverThread.interrupt();
                  try {
                      transceiverThread.join();
                  } catch (InterruptedException e) {
                      Thread.currentThread().interrupt();
                  }
                  transceiverThread = null;
              }
  
              locks.clear();
              flags.clear();
  
              isStarted = false;
          }
      }
  
      private void reset() {
          stop();
          isStarted = false;
          start();
      }
  
      public void registerHandler(KeContactHandler handler) {
          if (handler != null) {
              handlers.add(handler);
              locks.put(handler, new ReentrantLock());
  
              if (logger.isTraceEnabled()) {
                  logger.trace("There are now {} KEBA KeContact handlers registered with the transceiver",
                          handlers.size());
              }
  
              if (handlers.size() == 1) {
                  start();
              }
  
              if (!isConnected(handler)) {
                  establishConnection(handler);
              }
          }
      }
  
      public void unRegisterHandler(KeContactHandler handler) {
          if (handler != null) {
              locks.remove(handler);
              handlers.remove(handler);
  
              if (logger.isTraceEnabled()) {
                  logger.trace("There are now {} KEBA KeContact handlers registered with the transceiver",
                          handlers.size());
              }
  
              if (handlers.isEmpty()) {
                  stop();
              }
          }
      }
  
      protected ByteBuffer send(String message, KeContactHandler handler) {
          ReentrantLock handlerLock = locks.get(handler);
  
          if (handlerLock != null) {
              handlerLock.lock();
              try {
                  ByteBuffer buffer = ByteBuffer.allocate(message.getBytes().length);
                  buffer.put(message.getBytes("ASCII"));
  
                  flags.put(handler, Boolean.TRUE);
                  buffers.put(handler, buffer);
  
                  synchronized (handlerLock) {
                      if (logger.isTraceEnabled()) {
                          logger.trace("{} waiting on handerLock {}", Thread.currentThread().getName(),
                                  handlerLock.toString());
                      }
                      handlerLock.wait(KeContactHandler.REPORT_INTERVAL);
                  }
  
                  return buffers.remove(handler);
              } catch (UnsupportedEncodingException | InterruptedException e) {
                  Thread.currentThread().interrupt();
                  handler.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
              } finally {
                  handlerLock.unlock();
              }
          } else {
              if (logger.isDebugEnabled()) {
                  logger.debug("The handler for '{}' is not yet registered with the KeContactTransceiver",
                          handler.getThing().getUID());
              }
          }
          return null;
      }
  
      public Runnable transceiverRunnable = () -> {
          while (true) {
              try {
                  synchronized (selector) {
                      try {
                          selector.selectNow();
                      } catch (IOException e) {
                          logger.error("An exception occurred while selecting: {}", e.getMessage());
                      }
  
                      Iterator<SelectionKey> it = selector.selectedKeys().iterator();
                      while (it.hasNext()) {
                          SelectionKey selKey = it.next();
                          it.remove();
  
                          if (selKey.isValid() && selKey.isWritable()) {
                              DatagramChannel theChannel = (DatagramChannel) selKey.channel();
                              KeContactHandler theHandler = null;
                              boolean error = false;
  
                              for (KeContactHandler handler : handlers) {
                                  if (theChannel.equals(datagramChannels.get(handler))) {
                                      theHandler = handler;
                                      break;
                                  }
                              }
  
                              if (theHandler != null) {
                                  ReentrantLock theLock = locks.get(theHandler);
                                  Boolean theFlag = flags.get(theHandler);
                                  if (theLock != null && theLock.isLocked() && theFlag != null
                                          && theFlag.equals(Boolean.TRUE)) {
                                      ByteBuffer theBuffer = buffers.remove(theHandler);
                                      flags.put(theHandler, Boolean.FALSE);
  
                                      if (theBuffer != null) {
                                          try {
                                              theBuffer.rewind();
                                              logger.debug("Sending '{}' on the channel '{}'->'{}'",
                                                      new Object[] { new String(theBuffer.array()),
                                                              theChannel.getLocalAddress(),
                                                              theChannel.getRemoteAddress() });
                                              int byteswritten = theChannel.write(theBuffer);
                                          } catch (NotYetConnectedException e) {
                                              theHandler.updateStatus(ThingStatus.OFFLINE,
                                                      ThingStatusDetail.COMMUNICATION_ERROR,
                                                      "The remote host is not yet connected");
                                              error = true;
                                          } catch (ClosedChannelException e) {
                                              theHandler.updateStatus(ThingStatus.OFFLINE,
                                                      ThingStatusDetail.COMMUNICATION_ERROR,
                                                      "The connection to the remote host is closed");
                                              error = true;
                                          } catch (IOException e) {
                                              theHandler.updateStatus(ThingStatus.OFFLINE,
                                                      ThingStatusDetail.COMMUNICATION_ERROR, "An IO exception occurred");
                                              error = true;
                                          }
  
                                          if (error) {
                                              removeConnection(theHandler);
                                              establishConnection(theHandler);
                                          }
                                      }
                                  }
                              }
                          }
  
                          if (selKey.isValid() && selKey.isReadable()) {
                              int numberBytesRead = 0;
                              InetSocketAddress clientAddress = null;
                              ByteBuffer readBuffer = null;
                              boolean error = false;
  
                              if (selKey.equals(broadcastKey)) {
                                  try {
                                      readBuffer = ByteBuffer.allocate(BUFFER_SIZE);
                                      clientAddress = (InetSocketAddress) broadcastChannel.receive(readBuffer);
                                      logger.debug("Received {} from {} on the transceiver listener port ",
                                              new String(readBuffer.array()), clientAddress);
                                      numberBytesRead = readBuffer.position();
                                  } catch (IOException e) {
                                      logger.error(
                                              "An exception occurred while receiving data on the transceiver listener port: '{}'",
                                              e.getMessage(), e);
                                      error = true;
                                  }
  
                                  if (numberBytesRead == -1) {
                                      error = true;
                                  }
  
                                  if (!error) {
                                      readBuffer.flip();
                                      if (readBuffer.remaining() > 0) {
                                          for (KeContactHandler handler : handlers) {
                                              if (clientAddress != null && handler.getIPAddress()
                                                      .equals(clientAddress.getAddress().getHostAddress())) {
                                                  ReentrantLock theLock = locks.get(handler);
                                                  if (theLock != null && theLock.isLocked()) {
                                                      buffers.put(handler, readBuffer);
                                                      synchronized (theLock) {
                                                          if (logger.isTraceEnabled()) {
                                                              logger.trace("{} notifyall on handerLock {}",
                                                                      Thread.currentThread().getName(),
                                                                      theLock.toString());
                                                          }
                                                          theLock.notifyAll();
                                                      }
                                                  } else {
                                                      handler.onData(readBuffer);
                                                  }
                                              }
                                          }
                                      }
                                  } else {
                                      handlers.forEach(listener -> listener.updateStatus(ThingStatus.OFFLINE,
                                              ThingStatusDetail.COMMUNICATION_ERROR, "The transceiver is offline"));
                                      reset();
                                  }
                              } else {
                                  DatagramChannel theChannel = (DatagramChannel) selKey.channel();
                                  KeContactHandler theHandler = null;
  
                                  for (KeContactHandler handlers : handlers) {
                                      if (datagramChannels.get(handlers).equals(theChannel)) {
                                          theHandler = handlers;
                                          break;
                                      }
                                  }
  
                                  if (theHandler != null) {
                                      try {
                                          readBuffer = ByteBuffer.allocate(BUFFER_SIZE);
                                          numberBytesRead = theChannel.read(readBuffer);
                                          logger.debug("Received {} from {} on the transceiver listener port ",
                                                  new String(readBuffer.array()), theChannel.getRemoteAddress());
                                      } catch (NotYetConnectedException e) {
                                          theHandler.updateStatus(ThingStatus.OFFLINE,
                                                  ThingStatusDetail.COMMUNICATION_ERROR,
                                                  "The remote host is not yet connected");
                                          error = true;
                                      } catch (PortUnreachableException e) {
                                          theHandler.updateStatus(ThingStatus.OFFLINE,
                                                  ThingStatusDetail.CONFIGURATION_ERROR,
                                                  "The remote host is probably not a KEBA KeContact");
                                          error = true;
                                      } catch (IOException e) {
                                          theHandler.updateStatus(ThingStatus.OFFLINE,
                                                  ThingStatusDetail.COMMUNICATION_ERROR, "An IO exception occurred");
                                          error = true;
                                      }
  
                                      if (numberBytesRead == -1) {
                                          error = true;
                                      }
  
                                      if (!error) {
                                          readBuffer.flip();
                                          if (readBuffer.remaining() > 0) {
                                              ReentrantLock theLock = locks.get(theHandler);
  