@SuppressWarnings("unchecked")
  private Sysprop fetchFxRatesJSON() {
    Map<String, Object> map = new HashMap<String, Object>();
    Sysprop s = new Sysprop();
    ObjectReader reader = ParaObjectUtils.getJsonReader(Map.class);

    try {
      CloseableHttpClient http = HttpClients.createDefault();
      HttpGet httpGet = new HttpGet(SERVICE_URL);
      HttpResponse res = http.execute(httpGet);
      HttpEntity entity = res.getEntity();

      if (entity != null && Utils.isJsonType(entity.getContentType().getValue())) {
        JsonNode jsonNode = reader.readTree(entity.getContent());
        if (jsonNode != null) {
          JsonNode rates = jsonNode.get("rates");
          if (rates != null) {
            map = reader.treeToValue(rates, Map.class);
            s.setId(FXRATES_KEY);
            s.setProperties(map);
            //						s.addProperty("fetched", Utils.formatDate("dd MM yyyy HH:mm", Locale.UK));
            dao.create(s);
          }
        }
        EntityUtils.consume(entity);
      }
      logger.debug("Fetched rates from OpenExchange for {}.", new Date().toString());
    } catch (Exception e) {
      logger.error("TimerTask failed: {}", e);
    }
    return s;
  }
  @Override
  public void receiveLoop() {
    log.debug("receiveLoop starts");
    ObjectReader reader = this.mapper.reader();

    while (true) {
      try {
        JsonNode root = reader.readTree(this.istream);
        String type = root.path(TYPE).asText();
        JsonNode data = root.path(DATA);
        log.debug("Processing {} with {}", type, data);
        if (type.equals(GOODBYE)) {
          log.info("Connection closing from server.");
          break;
        } else if (type.equals(HELLO)) {
          this.receiveHello(data);
        } else if (type.equals(LOCALE)) {
          this.receiveHello(data);
        } else if (type.equals(PONG)) {
          // silently ignore
        } else if (!data.isMissingNode() || (root.isArray() && ((ArrayNode) root).size() > 0)) {
          // process replies with a data node or non-empty arrays
          JsonClientReply reply = new JsonClientReply(root);
          Runnable r = new RcvNotifier(reply, mLastSender, this);
          try {
            SwingUtilities.invokeAndWait(r);
          } catch (InterruptedException e) {
            log.error("Exception notifying listeners of reply: {}", e.getMessage(), e);
          } catch (InvocationTargetException e) {
            log.error("Exception notifying listeners of reply: {}", e.getMessage(), e);
          }
        }
      } catch (IOException e) {
        this.rcvException = true;
        reportReceiveLoopException(e);
        break;
      } catch (NoSuchElementException nse) {
        // we get an NSE when we are finished with this client
        // so break out of the loop
        break;
      }
    }
    ConnectionStatus.instance()
        .setConnectionState(this.controller.getCurrentPortName(), ConnectionStatus.CONNECTION_DOWN);
    log.error("Exit from rcv loop");
    this.recovery();
  }
  private void handleEvent(final HttpServerExchange exchange) throws Exception {
    try (final ChannelInputStream cis = new ChannelInputStream(exchange.getRequestChannel())) {
      final JsonNode payload = EVENT_PARAMETERS_READER.readTree(cis);
      final String generatedPageViewId = DivolteIdentifier.generate().value;

      final DivolteEvent.BrowserEventData browserEventData =
          new DivolteEvent.BrowserEventData(
              get(payload, "page_view_id", String.class).orElse(generatedPageViewId),
              get(payload, "location", String.class),
              get(payload, "referer", String.class),
              get(payload, "viewport_pixel_width", Integer.class),
              get(payload, "viewport_pixel_height", Integer.class),
              get(payload, "screen_pixel_width", Integer.class),
              get(payload, "screen_pixel_height", Integer.class),
              get(payload, "device_pixel_ratio", Integer.class));
      final DivolteEvent divolteEvent =
          new DivolteEvent(
              exchange,
              get(payload, "corrupt", Boolean.class).orElse(false),
              get(payload, "party_id", String.class)
                  .flatMap(DivolteIdentifier::tryParse)
                  .orElse(DivolteIdentifier.generate()),
              get(payload, "session_id", String.class)
                  .flatMap(DivolteIdentifier::tryParse)
                  .orElse(DivolteIdentifier.generate()),
              get(payload, "event_id", String.class).orElse(generatedPageViewId + "0"),
              ClientSideCookieEventHandler.EVENT_SOURCE_NAME,
              System.currentTimeMillis(),
              0L,
              get(payload, "new_party_id", Boolean.class).orElse(false),
              get(payload, "first_in_session", Boolean.class).orElse(false),
              get(payload, "event_type", String.class),
              () -> get(payload, "parameters", JsonNode.class),
              Optional.of(browserEventData));

      get(payload, "remote_host", String.class)
          .ifPresent(
              ip -> {
                try {
                  final InetAddress address = InetAddress.getByName(ip);
                  // We have no way of knowing the port
                  exchange.setSourceAddress(new InetSocketAddress(address, 0));
                } catch (final UnknownHostException e) {
                  log.warn("Could not parse remote host: " + ip, e);
                }
              });

      exchange.putAttachment(
          DUPLICATE_EVENT_KEY, get(payload, "duplicate", Boolean.class).orElse(false));

      exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json");
      exchange
          .getResponseChannel()
          .write(
              ByteBuffer.wrap(
                  mapper
                      .newRecordFromExchange(divolteEvent)
                      .toString()
                      .getBytes(StandardCharsets.UTF_8)));
      exchange.endExchange();
    }
  }