<commit_before>package i5.las2peer.webConnector.serviceManagement;

import java.io.Serializable;
import java.io.StringReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Timer;
import java.util.TimerTask;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import i5.las2peer.execution.NoSuchServiceMethodException;
import i5.las2peer.logging.L2pLogger;
import i5.las2peer.p2p.Node;
import i5.las2peer.p2p.ServiceNameVersion;
import i5.las2peer.persistency.EnvelopeException;
import i5.las2peer.restMapper.RESTMapper;
import i5.las2peer.restMapper.data.PathTree;
import i5.las2peer.security.ServiceInfoAgent;

public class ServiceRepositoryManager {

	public static final String SERVICE_SELFINFO_METHOD = "getRESTMapping";

	private static final L2pLogger logger = L2pLogger.getInstance(ServiceRepositoryManager.class.getName());

	private static final int DEFAULT_TIMER_INTERVAL_SECONDS = 300;
	private static int timerIntervalSeconds = DEFAULT_TIMER_INTERVAL_SECONDS;

	private HashMap<String, ServiceData> serviceRepository = new HashMap<String, ServiceData>();
	private PathTree tree = new PathTree();
	private Timer timer;

	public void start(Node node) throws Exception {
		start(node, DEFAULT_TIMER_INTERVAL_SECONDS);
	}

	public void start(Node node, int timerIntervalSeconds) throws Exception {
		ServiceRepositoryManager.timerIntervalSeconds = timerIntervalSeconds;
		startTimer(node);
	}

	public void stop() {
		stopTimer();
	}

	public boolean hasService(String serviceName) {
		if (!serviceRepository.containsKey(serviceName)) {
			return false;
		}
		return serviceRepository.get(serviceName).isActive();
	}

	public void manualUpdate(final Node node) throws Exception {
		executeTimer(node, getServiceInfoAgent(node));
	}

	private void executeTimer(Node node, ServiceInfoAgent finalAgent) {
		ServiceNameVersion[] services;
		try {
			services = ServiceInfoAgent.getServices();
		} catch (EnvelopeException e) {
			// do nothing for now
			e.printStackTrace();
			return;
		}
		logger.info("found " + services.length + " services on the network");
		synchronized (serviceRepository) {
			HashSet<String> checkedServices = new HashSet<>(serviceRepository.keySet());
			for (ServiceNameVersion currentService : services) {
				logger.info("handling service " + currentService.getNameVersion());
				String internalServiceName = getInternalServiceName(currentService.getName(),
						currentService.getVersion());
				if (!serviceRepository.containsKey(internalServiceName)) { // new service
					logger.info("adding new service " + internalServiceName);
					// add dummy element to repo to avoid duplicate scanning
					serviceRepository.put(internalServiceName,
							new ServiceData(currentService.getName(), currentService.getVersion(), true, null));
					try {
						String xml = (String) node.invokeGlobally(finalAgent, currentService, SERVICE_SELFINFO_METHOD,
								new Serializable[] {});
						if (xml == null || xml.isEmpty()) {
							System.err.println("Couldn't get xml mapping for " + currentService.getName()
									+ "! Please see log for details!");
						} else {
							try {
								// tree.merge(RESTMapper.getMappingTree(xml));
								ServiceData data = new ServiceData(currentService.getName(),
										currentService.getVersion(), true, xml);
								serviceRepository.put(internalServiceName, data);

								logger.info("adding xml for " + internalServiceName);
								addXML(new String[] { xml }); // for compatibility services: a service can also give XML
																// definition to other services
							} catch (Exception e) {
								// do nothing for now
								e.printStackTrace();
							}
						}
					} catch (NoSuchServiceMethodException e) {
						// expected, service doesn't provide a REST mapping
						logger.info("service " + internalServiceName + " doesn't provide a REST mapping");
					} catch (Exception e) {
						// do nothing for now
						e.printStackTrace();
					}
				} else if (!serviceRepository.get(internalServiceName).isActive()) { // enable not active services
					logger.info(internalServiceName + " is enabled again");
					serviceRepository.get(internalServiceName).enable();
				}
				checkedServices.remove(internalServiceName);
			}
			for (String service : checkedServices) {
				logger.info(service + " is disabled, because it seems not active anymore");
				serviceRepository.get(service).disable();
			}
		}
	}

	private void startTimer(final Node node) throws Exception {
		if (timer != null) {
			// timer is already running
			return;
		}
		final ServiceInfoAgent agent = getServiceInfoAgent(node);

		timer = new Timer();
		timer.scheduleAtFixedRate(new TimerTask() {
			public void run() {
				executeTimer(node, agent);
			}
		}, 0, // run first occurrence immediately
				timerIntervalSeconds * 1000); // run every x seconds
	}

	private void stopTimer() {
		if (timer != null) {
			timer.cancel();
		}
		timer = null;
	}

	private ServiceInfoAgent getServiceInfoAgent(Node node) throws Exception {
		ServiceInfoAgent agent;
		try {
			agent = ServiceInfoAgent.getServiceInfoAgent(ServiceInfoAgent.getDefaultPassphrase());

			if (!node.hasAgent(agent.getId())) {
				node.registerReceiver(agent);
			}<commit_msg>fix LAS-283 by waiting for initial service crawling<commit_after>package i5.las2peer.webConnector.serviceManagement;

import java.io.Serializable;
import java.io.StringReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Timer;
import java.util.TimerTask;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import i5.las2peer.execution.NoSuchServiceMethodException;
import i5.las2peer.logging.L2pLogger;
import i5.las2peer.p2p.Node;
import i5.las2peer.p2p.ServiceNameVersion;
import i5.las2peer.persistency.EnvelopeException;
import i5.las2peer.restMapper.RESTMapper;
import i5.las2peer.restMapper.data.PathTree;
import i5.las2peer.security.ServiceInfoAgent;

public class ServiceRepositoryManager {

	public static final String SERVICE_SELFINFO_METHOD = "getRESTMapping";

	private static final L2pLogger logger = L2pLogger.getInstance(ServiceRepositoryManager.class.getName());

	private static final int DEFAULT_TIMER_INTERVAL_SECONDS = 300;
	private static int timerIntervalSeconds = DEFAULT_TIMER_INTERVAL_SECONDS;

	private HashMap<String, ServiceData> serviceRepository = new HashMap<String, ServiceData>();
	private PathTree tree = new PathTree();
	private Timer timer;

	public void start(Node node) throws Exception {
		start(node, DEFAULT_TIMER_INTERVAL_SECONDS);
	}

	public void start(Node node, int timerIntervalSeconds) throws Exception {
		ServiceRepositoryManager.timerIntervalSeconds = timerIntervalSeconds;
		manualUpdate(node);
		startTimer(node);
	}

	public void stop() {
		stopTimer();
	}

	public boolean hasService(String serviceName) {
		if (!serviceRepository.containsKey(serviceName)) {
			return false;
		}
		return serviceRepository.get(serviceName).isActive();
	}

	public void manualUpdate(final Node node) throws Exception {
		executeTimer(node, getServiceInfoAgent(node));
	}

	private void executeTimer(Node node, ServiceInfoAgent finalAgent) {
		ServiceNameVersion[] services;
		try {
			services = ServiceInfoAgent.getServices();
		} catch (EnvelopeException e) {
			// do nothing for now
			e.printStackTrace();
			return;
		}
		logger.info("found " + services.length + " services on the network");
		synchronized (serviceRepository) {
			HashSet<String> checkedServices = new HashSet<>(serviceRepository.keySet());
			for (ServiceNameVersion currentService : services) {
				logger.info("handling service " + currentService.getNameVersion());
				String internalServiceName = getInternalServiceName(currentService.getName(),
						currentService.getVersion());
				if (!serviceRepository.containsKey(internalServiceName)) { // new service
					logger.info("adding new service " + internalServiceName);
					// add dummy element to repo to avoid duplicate scanning
					serviceRepository.put(internalServiceName,
							new ServiceData(currentService.getName(), currentService.getVersion(), true, null));
					try {
						String xml = (String) node.invokeGlobally(finalAgent, currentService, SERVICE_SELFINFO_METHOD,
								new Serializable[] {});
						if (xml == null || xml.isEmpty()) {
							System.err.println("Couldn't get xml mapping for " + currentService.getName()
									+ "! Please see log for details!");
						} else {
							try {
								// tree.merge(RESTMapper.getMappingTree(xml));
								ServiceData data = new ServiceData(currentService.getName(),
										currentService.getVersion(), true, xml);
								serviceRepository.put(internalServiceName, data);

								logger.info("adding xml for " + internalServiceName);
								addXML(new String[] { xml }); // for compatibility services: a service can also give XML
																// definition to other services
							} catch (Exception e) {
								// do nothing for now
								e.printStackTrace();
							}
						}
					} catch (NoSuchServiceMethodException e) {
						// expected, service doesn't provide a REST mapping
						logger.info("service " + internalServiceName + " doesn't provide a REST mapping");
					} catch (Exception e) {
						// do nothing for now
						e.printStackTrace();
					}
				} else if (!serviceRepository.get(internalServiceName).isActive()) { // enable not active services
					logger.info(internalServiceName + " is enabled again");
					serviceRepository.get(internalServiceName).enable();
				}
				checkedServices.remove(internalServiceName);
			}
			for (String service : checkedServices) {
				logger.info(service + " is disabled, because it seems not active anymore");
				serviceRepository.get(service).disable();
			}
		}
	}

	private void startTimer(final Node node) throws Exception {
		if (timer != null) {
			// timer is already running
			return;
		}
		final ServiceInfoAgent agent = getServiceInfoAgent(node);

		timer = new Timer();
		timer.scheduleAtFixedRate(new TimerTask() {
			public void run() {
				executeTimer(node, agent);
			}
		}, timerIntervalSeconds * 1000, // initial execution is triggered by start method
				timerIntervalSeconds * 1000); // run every x seconds
	}

	private void stopTimer() {
		if (timer != null) {
			timer.cancel();
		}
		timer = null;
	}

	private ServiceInfoAgent getServiceInfoAgent(Node node) throws Exception {
		ServiceInfoAgent agent;
		try {
			agent = ServiceInfoAgent.getServiceInfoAgent(ServiceInfoAgent.getDefaultPassphrase());

			if (!node.hasAgent(agent.getId())) {
				node.registerReceiver(agent);
			}
{'fraction_non_alphanumeric': 0.10253414809489576, 'fraction_numerical': 0.0066498921639108555, 'mean_word_length': 4.173872617387262, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 28}, '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': '4744579', 'n_tokens_mistral': 3732, 'n_tokens_neox': 3079, 'n_words': 764}