docs: @Override
      public synchronized void connect(Map<String, ?> env) throws IOException {
          // Connector can only be used once
          if (closed)
              throw new IOException();
  
          // connect has no effect if already connected
          if (connection != null)
              return;
  
          if (env != null)
              environment = env;
  
          // User credentials will either be available as a String[] for basic auth, OR
          //as a String (equals to ConnectorSettings.CERTIFICATE_AUTHENTICATION), which could also come from a system property
          Object credentials = getCredentials();
          if (!areRequiedCredentialsSet(credentials)) {
              throw new IOException(JMXConnector.CREDENTIALS + " not provided. Set to a String[2] {user,password}.");
          }
          // If the credentials are not certificate, extract and set the basicAuthHeader
          if (!ConnectorSettings.CERTIFICATE_AUTHENTICATION.equals(credentials)) {
              String[] userPass = (String[]) credentials;
              user = userPass[0];
              JSONConverter converter = JSONConverter.getConverter();
              try {
                  basicAuthHeader = "Basic " + converter.encodeStringAsBase64(user + ":" + userPass[1]);
              } catch (ConversionException ce) {
                  throw new IOException("Failure encoding credentials", ce);
              } finally {
                  JSONConverter.returnConverter(converter);
              }
          }
  
          hostnameVerificationDisabled = getBooleanSetting(ConnectorSettings.DISABLE_HOSTNAME_VERIFICATION, ClientConstants.DISABLE_HOSTNAME_VERIFICATION_DEFAULT);
          notificationDeliveryInterval = getIntegerSetting(ConnectorSettings.NOTIFICATION_DELIVERY_INTERVAL, ClientConstants.NOTIFICATION_DELIVERY_INTERVAL_DEFAULT);
          notificationFetchInterval = getIntegerSetting(ConnectorSettings.NOTIFICATION_FETCH_INTERVAL, ClientConstants.NOTIFICATION_FETCH_INTERVAL_DEFAULT);
          notificationInboxExpiry = getIntegerSetting(ConnectorSettings.NOTIFICATION_INBOX_EXPIRY, ClientConstants.NOTIFICATION_INBOX_EXPIRY_DEFAULT);
          readTimeout = getIntegerSetting(ConnectorSettings.READ_TIMEOUT, ClientConstants.READ_TIMEOUT_DEFAULT);
          notificationReadTimeout = getIntegerSetting(ConnectorSettings.NOTIFICATION_READ_TIMEOUT,
                                                      notificationDeliveryInterval > 0 ? 2 * notificationDeliveryInterval : ClientConstants.READ_TIMEOUT_DEFAULT);
          serverFailoverInterval = getIntegerSetting(ConnectorSettings.SERVER_FAILOVER_INTERVAL, ClientConstants.SERVER_FAILOVER_INTERVAL_DEFAULT);
          maxServerWaitTime = getIntegerSetting(ConnectorSettings.MAX_SERVER_WAIT_TIME, ClientConstants.MAX_SERVER_WAIT_TIME_DEFAULT);
          serverStatusPollingInterval = getIntegerSetting(ConnectorSettings.SERVER_STATUS_POLLING_INTERVAL, ClientConstants.SERVER_STATUS_POLLING_INTERVAL_DEFAULT);
  
          //Validate endpoints if applicable
          Object list = environment.get(ConnectorSettings.WLM_ENDPOINTS);
          if (list != null && list instanceof List) {
              validateEndpointList((List<Object>) list);
          }
  
          // -- Logging
          if (logger.isLoggable(Level.FINER)) {
              StringBuilder sb = new StringBuilder();
              sb.append("Endpoints:");
              for (String endpoint : wlmList) {
                  sb.append(endpoint);
                  sb.append("  ");
              }
              logger.logp(Level.FINER, logger.getName(), "connect", sb.toString());
  
              if (logger.isLoggable(Level.FINEST)) {
                  sb = new StringBuilder();
                  sb.append("notificationDeliveryInterval");
                  sb.append("=");
                  sb.append(notificationDeliveryInterval);
                  sb.append("\n");
  
                  sb.append("notificationFetchInterval");
                  sb.append("=");
                  sb.append(notificationFetchInterval);
                  sb.append("\n");
  
                  sb.append("notificationInboxExpiry");
                  sb.append("=");
                  sb.append(notificationInboxExpiry);
                  sb.append("\n");
  
                  sb.append("readTimeout");
                  sb.append("=");
                  sb.append(readTimeout);
                  sb.append("\n");
  
                  sb.append("notificationReadTimeout");
                  sb.append("=");
                  sb.append(notificationReadTimeout);
                  sb.append("\n");
  
                  sb.append("serverFailoverInterval");
                  sb.append("=");
                  sb.append(serverFailoverInterval);
                  sb.append("\n");
  
                  sb.append("maxServerWaitTime");
                  sb.append("=");
                  sb.append(maxServerWaitTime);
                  sb.append("\n");
  
                  sb.append("serverStatusPollingInterval");
                  sb.append("=");
                  sb.append(serverStatusPollingInterval);
                  sb.append("\n");
  
                  logger.logp(Level.FINEST, logger.getName(), "connect", sb.toString());
              }
          }
  
          connectionId = getConnectionNumber();
  
          Object o = environment.get(ConnectorSettings.CUSTOM_SSLSOCKETFACTORY);
          if (o != null && o instanceof SSLSocketFactory) {
              SSLSocketFactory sslf = (SSLSocketFactory) o;
              this.setCustomSSLSocketFactory(sslf);
          }
  
          // No longer need environment
          environment = null;
  
          connection = new RESTMBeanServerConnection(this);
  
          connectionOpened();
      }