<commit_before>package io.github.furti.jmxhealth.server;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.servlet.ServletContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.context.ServletContextAware;

import com.fasterxml.jackson.databind.ObjectMapper;

import io.github.furti.jmxhealth.AttributeState;
import io.github.furti.jmxhealth.server.config.RemoteConfig;
import io.github.furti.jmxhealth.server.config.RemoteServer;

@Service
public class JmxManager implements ServletContextAware {
	private static final Logger LOG = LoggerFactory.getLogger(JmxManager.class);

	private static final String CONFIG_KEY = "io.github.furti.jmxhealth.data-location";
	private String dataLocation = null;
	private ObjectMapper objectMapper;
	private List<RemoteConnection> connections;
	private StateManager stateManager;
	private List<RemoteServer> failedConnections;
	private boolean initialized = false;

	@Autowired
	public JmxManager(ObjectMapper objectMapper, StateManager stateManager) {
		this.objectMapper = objectMapper;
		this.stateManager = stateManager;
	}

	@Scheduled(fixedDelay = 10000, initialDelay = 1000)
	public void pollServers() {
		if (!initialized) {
			return;
		}

		this.reconnectFailedConnections();

		if (this.connections == null) {
			return;
		}

		for (RemoteConnection connection : this.connections) {
			try {
				List<AttributeState> result = connection.poll();

				System.out.println(result);
			} catch (Exception ex) {
				// TODO: handle failed state
			}
		}
	}

	@PostConstruct
	public void configureRemotes() {
		try {
			Assert.notNull(dataLocation, "Data location must not be null. Set the path to the data file via "
					+ CONFIG_KEY + " System Property or Servlet Config Attribute.");

			RemoteConfig config = objectMapper.readValue(new File(dataLocation + "/jmxhealth.json"),
					RemoteConfig.class);

			this.setupConnections(config);
		} catch (Exception ex) {
			LOG.error("Error setting up JMX Connections", ex);
			this.stateManager.failed("ServerState", "Error setting up JMX Connections", ex);
		}
	}

	private void setupConnections(RemoteConfig config) {
		if (config.getServers() == null || config.getServers().isEmpty()) {
			return;
		}

		connections = new ArrayList<>(config.getServers().size());

		config.getServers().forEach((server) -> {
			try {
				this.connectToServer(server);
			} catch (Exception e) {
				if (failedConnections == null) {
					failedConnections = new ArrayList<>();
				}

				failedConnections.add(server);

				LOG.error("Error connection to " + server, e);

				this.stateManager.failed("Connection-" + server.getHost() + ":" + server.getPort(),
						"Error connection to remote server", e);
			}
		});

		this.initialized = true;
	}

	private void reconnectFailedConnections() {
		if (this.failedConnections == null || this.failedConnections.isEmpty()) {
			return;
		}

		Iterator<RemoteServer> it = this.failedConnections.iterator();

		while (it.hasNext()) {
			RemoteServer server = it.next();
			try {
				this.connectToServer(server);
				it.remove();

				this.stateManager.removeFailedState("Connection-" + server.getHost() + ":" + server.getPort());
			} catch (Exception e) {
				LOG.error("Error connection to " + server, e);
				
				this.stateManager.failed("Connection-" + server.getHost() + ":" + server.getPort(),
						"Error connection to remote server", e);
			}
		}
	}

	private void connectToServer(RemoteServer server) throws IOException {
		this.validateServerConfig(server);

		RemoteConnection connection = this.createConnection(server);

		connections.add(connection);
	}

	private void validateServerConfig(RemoteServer server) {
		if (server.getWatchers() == null || server.getWatchers().isEmpty()) {
			throw new IllegalArgumentException("No Watchers are defined for " + server);<commit_msg>Improved error message for connection exceptions<commit_after>package io.github.furti.jmxhealth.server;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.servlet.ServletContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.context.ServletContextAware;

import com.fasterxml.jackson.databind.ObjectMapper;

import io.github.furti.jmxhealth.AttributeState;
import io.github.furti.jmxhealth.server.config.RemoteConfig;
import io.github.furti.jmxhealth.server.config.RemoteServer;

@Service
public class JmxManager implements ServletContextAware {
	private static final Logger LOG = LoggerFactory.getLogger(JmxManager.class);

	private static final String CONFIG_KEY = "io.github.furti.jmxhealth.data-location";
	private String dataLocation = null;
	private ObjectMapper objectMapper;
	private List<RemoteConnection> connections;
	private StateManager stateManager;
	private List<RemoteServer> failedConnections;
	private boolean initialized = false;

	@Autowired
	public JmxManager(ObjectMapper objectMapper, StateManager stateManager) {
		this.objectMapper = objectMapper;
		this.stateManager = stateManager;
	}

	@Scheduled(fixedDelay = 10000, initialDelay = 1000)
	public void pollServers() {
		if (!initialized) {
			return;
		}

		this.reconnectFailedConnections();

		if (this.connections == null) {
			return;
		}

		for (RemoteConnection connection : this.connections) {
			try {
				List<AttributeState> result = connection.poll();

				System.out.println(result);
			} catch (Exception ex) {
				LOG.error("Error polling " + connection);
			}
		}
	}

	@PostConstruct
	public void configureRemotes() {
		try {
			Assert.notNull(dataLocation, "Data location must not be null. Set the path to the data file via "
					+ CONFIG_KEY + " System Property or Servlet Config Attribute.");

			RemoteConfig config = objectMapper.readValue(new File(dataLocation + "/jmxhealth.json"),
					RemoteConfig.class);

			this.setupConnections(config);
		} catch (Exception ex) {
			LOG.error("Error setting up JMX Connections", ex);
			this.stateManager.failed("ServerState", "Error setting up JMX Connections", ex);
		}
	}

	private void setupConnections(RemoteConfig config) {
		if (config.getServers() == null || config.getServers().isEmpty()) {
			return;
		}

		connections = new ArrayList<>(config.getServers().size());

		config.getServers().forEach((server) -> {
			try {
				this.connectToServer(server);
			} catch (Exception e) {
				if (failedConnections == null) {
					failedConnections = new ArrayList<>();
				}

				failedConnections.add(server);

				LOG.error("Error connection to " + server, e);

				this.stateManager.failed(server.toString(), "Error connecting to remote server", e);
			}
		});

		this.initialized = true;
	}

	private void reconnectFailedConnections() {
		if (this.failedConnections == null || this.failedConnections.isEmpty()) {
			return;
		}

		Iterator<RemoteServer> it = this.failedConnections.iterator();

		while (it.hasNext()) {
			RemoteServer server = it.next();
			try {
				this.connectToServer(server);
				it.remove();

				this.stateManager.removeFailedState(server.toString());
			} catch (Exception e) {
				LOG.error("Error connection to " + server, e);

				this.stateManager.failed(server.toString(), "Error connecting to remote server", e);
			}
		}
	}

	private void connectToServer(RemoteServer server) throws IOException {
		this.validateServerConfig(server);

		RemoteConnection connection = this.createConnection(server);

		connections.add(connection);
	}

	private void validateServerConfig(RemoteServer server) {
		if (server.getWatchers() == null || server.getWatchers().isEmpty()) {
			throw new IllegalArgumentException("No Watchers are defined for " + server);
{'fraction_non_alphanumeric': 0.11333794056668971, 'fraction_numerical': 0.002533978346003225, 'mean_word_length': 4.708744247205786, 'pattern_counts': {'":': 3, '<': 15, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3143748', 'n_tokens_mistral': 2789, 'n_tokens_neox': 2456, 'n_words': 561}