code: @Override
      public Future<Long> ping() {
          synchronized (pingLock) {
              if (pingTask == null || pingTask.isDone()) {
                  pingTask = GlobalCachedExecutorService.submit(() -> {
                      final ConnectionState.State previousConnectionState = connectionState;
                      try {
                          validateMiddleware();
                          Long requestTime = remoteServer.call("ping", System.currentTimeMillis(), JPService.testMode() ? PING_TEST_TIMEOUT : PING_TIMEOUT);
                          lastPingReceived = System.currentTimeMillis();
                          connectionPing = lastPingReceived - requestTime;
                          return connectionPing;
                      } catch (TimeoutException ex) {
                          synchronized (connectionMonitor) {
                              /**
                               * After a ping timeout we should switch back to {@code connectionState == CONNECTING} because the controller is not reachable any longer.
                               * There is only one edge case when the ping was triggered before the controller was fully started.
                               * Than we switch to connected when the notification is received after controller startup, but the ping timeout would again result in a reconnect.
                               * This is avoided by storing and comparing the {@code previousConnectionState} state and by only switching to {@code connectionState == CONNECTING} if the connection was previously established.
                               */
                              if (previousConnectionState == CONNECTED && connectionState == CONNECTED) {
                                  logger.warn("Remote connection to Controller[" + ScopeTransformer.transform(getScope()) + "] lost!");
  
                                  // init reconnection
                                  setConnectionState(CONNECTING);
                              }
                          }
                          throw ex;
                      } catch (final InvalidStateException ex) {
                          // reinit remote service because middleware connection lost!
                          switch (this.connectionState) {
                              // only if the connection was established before and no reconnect is ongoing.
                              case CONNECTING:
                              case CONNECTED:
                                  try {
                                      reinit();
                                  } catch (final CouldNotPerformException exx) {
                                      ExceptionPrinter.printHistory("Recovering middleware connection failed!", exx, logger);
                                  }
                          }
                          throw ex;
                      } catch (CouldNotPerformException ex) {
                          throw new CouldNotPerformException("ping failed", ex);
                      }
                  });
              }
              return pingTask;
          }
      }
docs: {@inheritDoc}
  
  @return {@inheritDoc}