gt:         try (HttpRequest req = new HttpRequest(clientBuilder)) {
context: /**
   * Copyright (c) 2010-2021 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.io.neeo.internal;
  
  import java.beans.PropertyChangeListener;
  import java.beans.PropertyChangeSupport;
  import java.io.IOException;
  import java.net.InetSocketAddress;
  import java.net.Socket;
  import java.net.URL;
  import java.util.Map;
  import java.util.Objects;
  import java.util.concurrent.Future;
  import java.util.concurrent.ScheduledExecutorService;
  import java.util.concurrent.ScheduledFuture;
  import java.util.concurrent.TimeUnit;
  import java.util.concurrent.atomic.AtomicBoolean;
  import java.util.concurrent.atomic.AtomicReference;
  
  import javax.ws.rs.client.ClientBuilder;
  
  import org.eclipse.jdt.annotation.NonNullByDefault;
  import org.eclipse.jdt.annotation.Nullable;
  import org.eclipse.jetty.http.HttpStatus;
  import org.openhab.core.common.ThreadPoolManager;
  import org.openhab.core.id.InstanceUUID;
  import org.openhab.core.net.HttpServiceUtil;
  import org.openhab.io.neeo.internal.models.NeeoAdapterRegistration;
  import org.openhab.io.neeo.internal.models.NeeoRecipe;
  import org.openhab.io.neeo.internal.models.NeeoRecipeUrls;
  import org.openhab.io.neeo.internal.models.NeeoSystemInfo;
  import org.openhab.io.neeo.internal.net.HttpRequest;
  import org.openhab.io.neeo.internal.net.HttpResponse;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import com.google.gson.Gson;
  import com.google.gson.JsonElement;
  import com.google.gson.JsonObject;
  import com.google.gson.JsonParser;
  
  /**
   * The class provides the API for communicating with a NEEO brain
   *
   * @author Tim Roberts - Initial Contribution
   */
  @NonNullByDefault
  public class NeeoApi implements AutoCloseable {
  
      /** The property name used for connection change notifications */
      public static final String CONNECTED = "connected";
  
      /** The logger */
      private final Logger logger = LoggerFactory.getLogger(NeeoApi.class);
  
      /** The gson used in communications */
      private static final Gson GSON = NeeoUtil.createGson();
  
      /** The brain's IP address */
      private final String brainIpAddress;
  
      private final ClientBuilder clientBuilder;
  
      /** The URL of the brain */
      private final String brainUrl;
  
      /** The brain identifier */
      private final String brainId;
  
      /** The name of the brain */
      private final String brainName;
  
      /** The known device keys on the brain. */
      private final NeeoDeviceKeys deviceKeys;
  
      /** The brain's system information */
      private final NeeoSystemInfo systemInfo;
  
      /** The property change support */
      private final PropertyChangeSupport propertySupport = new PropertyChangeSupport(this);
  
      /** The interval, in seconds, to do a check status call */
      private final int checkStatusInterval;
  
      /** The callback url registered with the brain */
      private final URL callbackUrl;
  
      /** The ansi escape color conversion */
      private static final String[] ANSICOLORS = new String[] { "black", "red", "green", "yellow", "blue", "magenta",
              "cyan", "white" };
  
      /** The scheduler used to schedule tasks */
      private final ScheduledExecutorService scheduler = ThreadPoolManager
              .getScheduledPool(NeeoConstants.THREAD_POOL_NAME);
  
      /** The connection task (not-null when connecting, null otherwise) */
      private final AtomicReference<@Nullable Future<?>> connect = new AtomicReference<>(null);
  
      /** The check status task (not-null when connecting, null otherwise) */
      private final AtomicReference<@Nullable ScheduledFuture<?>> checkStatus = new AtomicReference<>(null);
  
      /** The {@link HttpRequest} used for making requests */
      private final AtomicReference<HttpRequest> request;
      /** Whether the brain is currently connected */
      private final AtomicBoolean connected = new AtomicBoolean(false);
  
      /**
       * Constructs the APi from the given IP address, brain identifier and {@link ServiceContext}
       *
       * @param ipAddress the non-empty ip address
       * @param brainId the non-empty brain id
       * @param context the non-null {@link ServiceContext}
       * @throws IOException if an exception occurs connecting to the brain
       */
      public NeeoApi(String ipAddress, String brainId, ServiceContext context, ClientBuilder clientBuilder)
              throws IOException {
          NeeoUtil.requireNotEmpty(ipAddress, "ipAddress cannot be empty");
          NeeoUtil.requireNotEmpty(brainId, "brainId cannot be empty");
          Objects.requireNonNull(context, "context cannot be null");
  
          this.brainIpAddress = ipAddress;
          this.brainId = brainId;
          this.clientBuilder = clientBuilder;
          this.brainUrl = NeeoConstants.PROTOCOL + (ipAddress.startsWith("/") ? ipAddress.substring(1) : ipAddress) + ":"
                  + NeeoConstants.DEFAULT_BRAIN_PORT;
          deviceKeys = new NeeoDeviceKeys(brainUrl, clientBuilder);
  
          request = new AtomicReference<>(new HttpRequest(clientBuilder));
  
          this.systemInfo = getSystemInfo(ipAddress, clientBuilder);
  
          String name = brainId;
          try (HttpRequest request = new HttpRequest(clientBuilder)) {
              logger.debug("Getting existing device mappings from {}{}", brainUrl, NeeoConstants.PROJECTS_HOME);
              final HttpResponse resp = request.sendGetCommand(brainUrl + NeeoConstants.PROJECTS_HOME);
              if (resp.getHttpCode() != HttpStatus.OK_200) {
                  throw resp.createException();
              }
  
              final JsonObject root = JsonParser.parseString(resp.getContent()).getAsJsonObject();
              for (Map.Entry<String, JsonElement> room : root.getAsJsonObject("rooms").entrySet()) {
                  final JsonObject roomObj = (JsonObject) room.getValue();
  
                  if (roomObj.get("hasController").getAsBoolean()) {
                      name = roomObj.get("name").getAsString();
                      break;
                  }
              }
          }
          this.brainName = name;
  
          final Object statusCheck = context.getComponentContext().getProperties()
                  .get(NeeoConstants.CFG_CHECKSTATUSINTERVAL);
          int checkStatus = 10;
          if (statusCheck != null) {
              try {
                  checkStatus = Integer.parseInt(statusCheck.toString());
              } catch (NumberFormatException e) {
                  logger.debug("{} was not a valid integer, defaulting to 10: {}", NeeoConstants.CFG_CHECKSTATUSINTERVAL,
                          statusCheck);
              }
          }
          checkStatusInterval = checkStatus;
  
          // Get the port the service is listening on
          final int port = HttpServiceUtil.getHttpServicePort(context.getComponentContext().getBundleContext());
  
          final String primaryAddress = context.getNetworkAddressService().getPrimaryIpv4HostAddress();
          if (primaryAddress == null) {
              throw new IOException(
                      "Unable to create a callback URL because there is no primary address specified (please set the primary address in the configuration)");
          }
  
          callbackUrl = new URL("http://" + primaryAddress + ":"
                  + (port == -1 ? NeeoConstants.DEFAULT_OPENHAB_PORT : port) + NeeoUtil.getServletUrl(brainId));
      }
  
      /**
       * Returns the brain's system information
       *
       * @return a non-null system information
       */
      public NeeoSystemInfo getSystemInfo() {
          return this.systemInfo;
      }
  
      /**
       * Helper method to retrieve the {@link NeeoSystemInfo} from the given ip address
       *
       * @param ipAddress the non-empty ip address
       * @return the non-null {@link NeeoSystemInfo} for the address
       * @throws IOException Signals that an I/O exception has occurred or the URL is not a brain
       */
      public static NeeoSystemInfo getSystemInfo(String ipAddress, ClientBuilder clientBuilder) throws IOException {
          NeeoUtil.requireNotEmpty(ipAddress, "ipAddress cannot be empty");
          final String sysInfo = NeeoConstants.PROTOCOL + (ipAddress.startsWith("/") ? ipAddress.substring(1) : ipAddress)
                  + ":" + NeeoConstants.DEFAULT_BRAIN_PORT + NeeoConstants.SYSTEMINFO;
  
  