it.polimi.tower4clouds.manager.MonitoringManager.java Source code

Java tutorial

Introduction

Here is the source code for it.polimi.tower4clouds.manager.MonitoringManager.java

Source

/**
 * Copyright (C) 2014 Politecnico di Milano (marco.miglierina@polimi.it)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package it.polimi.tower4clouds.manager;

import it.polimi.deib.csparql_rest_api.RSP_services_csparql_API;
import it.polimi.deib.csparql_rest_api.exception.ObserverErrorException;
import it.polimi.deib.csparql_rest_api.exception.QueryErrorException;
import it.polimi.deib.csparql_rest_api.exception.ServerErrorException;
import it.polimi.deib.csparql_rest_api.exception.StreamErrorException;
import it.polimi.modaclouds.monitoring.kb.api.DeserializationException;
import it.polimi.modaclouds.monitoring.kb.api.KbAPI;
import it.polimi.modaclouds.monitoring.kb.api.RspKbAPI;
import it.polimi.modaclouds.monitoring.kb.api.SerializationException;
import it.polimi.modaclouds.qos_models.Problem;
import it.polimi.tower4clouds.common.net.NetUtil;
import it.polimi.tower4clouds.manager.api.IManagerAPI;
import it.polimi.tower4clouds.manager.api.NotFoundException;
import it.polimi.tower4clouds.manager.api.Observer;
import it.polimi.tower4clouds.manager.api.SocketProtocol;
import it.polimi.tower4clouds.model.data_collectors.DCConfiguration;
import it.polimi.tower4clouds.model.data_collectors.DCDescriptor;
import it.polimi.tower4clouds.model.ontology.MO;
import it.polimi.tower4clouds.model.ontology.MOVocabulary;
import it.polimi.tower4clouds.model.ontology.Resource;
import it.polimi.tower4clouds.rdf_history_db.api.RdfHistoryDBAPI;
import it.polimi.tower4clouds.rules.AbstractAction;
import it.polimi.tower4clouds.rules.MonitoredTarget;
import it.polimi.tower4clouds.rules.MonitoringRule;
import it.polimi.tower4clouds.rules.MonitoringRules;
import it.polimi.tower4clouds.rules.Parameter;
import it.polimi.tower4clouds.rules.RulesValidator;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.commons.beanutils.PropertyUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

public class MonitoringManager implements IManagerAPI {

    private static final int MAX_KEEP_ALIVE = Integer.MAX_VALUE / 1000;
    private Logger logger = LoggerFactory.getLogger(this.getClass().getName());
    private final ScheduledExecutorService keepAliveScheduler;

    private QueryFactory queryFactory;

    private Map<String, String> streamsByMetric = new HashMap<String, String>();
    private Map<String, DCConfiguration> dCConfigByRuleId = new HashMap<String, DCConfiguration>();
    private Map<String, MonitoringRule> rulesByRuleId = new HashMap<String, MonitoringRule>();
    private Map<String, String> queryIdByRuleId = new HashMap<String, String>();
    private Map<String, Query> queryByQueryId = new HashMap<String, Query>();
    private Map<String, String> ruleIdByObservableMetric = new HashMap<String, String>();
    private Map<String, Set<String>> inputMetricsByRuleId = new HashMap<String, Set<String>>();
    private Map<String, Set<String>> rulesIdsByInputMetric = new HashMap<String, Set<String>>();

    private Map<String, Set<String>> observersIdsByMetric = new HashMap<String, Set<String>>();
    private Map<String, Observer> observersById = new HashMap<String, Observer>();

    private final int keepAliveCheckPeriod = 60;

    private Map<String, DCDescriptor> registeredDCs;
    private Map<String, Integer> dcsKeepAlive;
    private Map<String, Long> dcsKATimestamp;
    private Map<String, Resource> registeredResources;
    private Map<String, Integer> resourcesKeepAlive;
    private Map<String, Long> resourcesKATimestamp;

    private final Object dcAndResourcesLock = new Object();

    private RulesValidator validator;

    private KbAPI knowledgeBase;
    private RSP_services_csparql_API dataAnalyzer;
    private RdfHistoryDBAPI rdfHistoryDB;

    private ManagerConfig config;

    private ExecutorService actionsExecutor;

    public MonitoringManager(ManagerConfig config) throws Exception {
        this.config = config;
        validator = new RulesValidator();

        RspKbAPI.KB_NAME_SPACE = MO.URI;
        RspKbAPI.KB_NS_PREFIX = MO.prefix;

        knowledgeBase = new RspKbAPI(config.getDaUrl());
        dataAnalyzer = new RSP_services_csparql_API(config.getDaUrl());

        logger.info("Checking if Data Analyzer is reachable");
        NetUtil.waitForResponseCode(config.getDaUrl() + "/queries", 200, MAX_KEEP_ALIVE, 5000);
        logger.info("Resetting KB");
        knowledgeBase.clearAll();
        logger.info("Resetting DA");
        resetDA();

        registeredDCs = new ConcurrentHashMap<String, DCDescriptor>();
        dcsKeepAlive = new ConcurrentHashMap<String, Integer>();
        dcsKATimestamp = new ConcurrentHashMap<String, Long>();
        registeredResources = new ConcurrentHashMap<String, Resource>();
        resourcesKeepAlive = new ConcurrentHashMap<String, Integer>();
        resourcesKATimestamp = new ConcurrentHashMap<String, Long>();

        queryFactory = new QueryFactory();

        actionsExecutor = Executors.newFixedThreadPool(1);
        keepAliveScheduler = Executors.newScheduledThreadPool(1);
        keepAliveScheduler.scheduleAtFixedRate(new keepAliveChecker(), 0, keepAliveCheckPeriod, TimeUnit.SECONDS);

        logger.info("Uploading ontology to KB");
        knowledgeBase.putModel(MO.model, ManagerConfig.MODEL_GRAPH_NAME);

        if (config.getRdfHistoryDbIP() != null) {
            rdfHistoryDB = new RdfHistoryDBAPI(config.getRdfHistoryDbIP(), config.getRdfHistoryDbPort());
            rdfHistoryDB.setAsync(true);
        }
        // initSelfMonitoring();
    }

    // private void initSelfMonitoring() {
    // Map<Property, String> applicationProperties = new HashMap<Property,
    // String>();
    // applicationProperties.put(Property.ID, "Manager");
    // applicationProperties.put(Property.TYPE, "Tower4Clouds");
    // Registry.initialize("localhost", config.getMmPort(),
    // applicationProperties, getClass().getPackage().getName());
    // Registry.startMonitoring();
    // }

    public synchronized void resetDA() {
        try {
            JsonParser parser = new JsonParser();
            JsonArray jsonQueriesInfoArray = parser.parse(dataAnalyzer.getQueriesInfo()).getAsJsonArray();
            for (JsonElement jsonElement : jsonQueriesInfoArray) {
                JsonObject queryInfoJson = jsonElement.getAsJsonObject();
                String queryId = queryInfoJson.get("id").getAsString();
                dataAnalyzer.unregisterQuery(prepareQueryURI(queryId));
            }
            JsonArray jsonStreamsInfoArray = parser.parse(dataAnalyzer.getStreamsInfo()).getAsJsonArray();
            for (JsonElement jsonElement : jsonStreamsInfoArray) {
                String streamId = jsonElement.getAsJsonObject().get("streamIRI").getAsString();
                dataAnalyzer.unregisterStream(streamId);
            }
        } catch (Exception e) {
            logger.error("An error occurred while trying to clear the DDA", e);
        }
    }

    @Override
    public synchronized void installRules(MonitoringRules rules) throws RuleInstallationException {
        logger.info("{} rule(s) to install", rules.getMonitoringRules().size());
        logger.info("Validating rules");
        validate(rules);
        String installedRules = "";
        try {
            for (MonitoringRule rule : rules.getMonitoringRules()) {
                installRule(rule);
                installedRules += " " + rule.getId();
            }
        } catch (RuleInstallationException e) {
            throw new RuleInstallationException(
                    "Error while installing rules, only the following rules were successfully installed:"
                            + installedRules + ". Problems: " + e.getMessage(),
                    e);
        }
    }

    private void installRule(MonitoringRule rule) throws RuleInstallationException {
        logger.info("Installing rule {}", rule.getId());
        try {
            Query query = queryFactory.prepareQuery(rule);

            queryIdByRuleId.put(rule.getId(), query.getId());

            Set<String> inputMetrics = query.getRequiredMetrics();
            logger.debug("Input metrics: {}", inputMetrics.toString());
            for (String inMetric : inputMetrics) {
                DCConfiguration newDCConfig = prepareDCConfig(rule, inMetric);
                if (!streamsByMetric.containsKey(inMetric)) {
                    String newStream = prepareStreamURI(inMetric);
                    logger.debug("Registering stream {}", inMetric, newStream, newDCConfig);
                    dataAnalyzer.registerStream(newStream);
                    streamsByMetric.put(inMetric, newStream);
                }
                addInputMetric(inMetric, rule.getId());
                synchronized (dcAndResourcesLock) {
                    dCConfigByRuleId.put(rule.getId(), newDCConfig);
                    logger.debug("Saving DC configuration: {}", newDCConfig);
                }
                String inputStream = streamsByMetric.get(inMetric);
                logger.debug("Adding input stream {} to query", inputStream);
                query.addInputStreamURI(inputStream);
            }
            String csparqlQuery = query.build();
            logger.debug("Installing query:\n{}", csparqlQuery);
            dataAnalyzer.registerQuery(query.getId(), csparqlQuery);
            queryByQueryId.put(query.getId(), query);
            if (query.hasOutputMetric()) {
                String outputMetric = query.getOutputMetric();
                logger.debug("Output metric: {}", outputMetric);
                if (streamsByMetric.containsKey(outputMetric)) {
                    String message = "The metric " + outputMetric + " is alread produced by ";
                    if (ruleIdByObservableMetric.containsKey(outputMetric)) {
                        message += "rule " + ruleIdByObservableMetric.get(outputMetric);
                    } else {
                        message += "some data collector according to rules "
                                + getRulesIdsFromInputMetric(outputMetric).toString();
                    }
                    throw new RuleInstallationException(message);
                }
                String outputStreamURI = prepareStreamURI(query.getId());
                streamsByMetric.put(outputMetric, outputStreamURI);
                ruleIdByObservableMetric.put(outputMetric, rule.getId());
            }
            if (query.hasActions()) {
                dataAnalyzer.addHttpObserver(prepareQueryURI(query.getId()), "http://" + config.getMmIP() + ":"
                        + config.getMmPort() + "/v1/monitoring-rules/" + rule.getId() + "/actions", "TOWER/JSON");
            }
            rulesByRuleId.put(rule.getId(), rule);
        } catch (Exception e) {
            logger.error("Error while installing rule {}, rolling back", rule.getId(), e);
            try {
                cleanUpRule(rule.getId());
            } catch (IOException e1) {
                throw new RuleInstallationException(
                        "Something went wrong while rolling back the installation of rule " + rule.getId()
                                + ", try uninstalling and reinstalling the rule",
                        e);
            }
            throw new RuleInstallationException(e);
        }
    }

    private void addInputMetric(String inMetric, String ruleId) {
        Set<String> iMetrics = inputMetricsByRuleId.get(ruleId);
        if (iMetrics == null) {
            iMetrics = new HashSet<String>();
            inputMetricsByRuleId.put(ruleId, iMetrics);
        }
        iMetrics.add(inMetric);

        Set<String> rulesIds = rulesIdsByInputMetric.get(inMetric);
        if (rulesIds == null) {
            rulesIds = new HashSet<String>();
            rulesIdsByInputMetric.put(inMetric, rulesIds);
        }
        rulesIds.add(ruleId);
    }

    private Set<String> getRulesIdsFromInputMetric(String iMetric) {
        HashSet<String> ids = new HashSet<String>();
        for (Query query : queryByQueryId.values()) {
            if (query.getRequiredMetrics().contains(iMetric)) {
                ids.add(query.getRuleId());
            }
        }
        return ids;
    }

    // private String getRuleIdFromQueryId(String queryId) {
    // for (Entry<String, Set<String>> entry : queryByRuleId.entrySet()) {
    // if (entry.getValue().contains(queryId))
    // return entry.getKey();
    // }
    // return null;
    // }

    // private void addQueryIdByRuleId(String ruleId, String queryId) {
    // Set<String> queriesIds = queryByRuleId.get(ruleId);
    // if (queriesIds == null) {
    // queriesIds = new HashSet<String>();
    // queryByRuleId.put(ruleId, queriesIds);
    // }
    // queriesIds.add(queryId);
    // }

    private DCConfiguration prepareDCConfig(MonitoringRule rule, String metric) {
        // TODO currently only one collected metric is allowed
        DCConfiguration dc = new DCConfiguration();
        for (Parameter parameter : rule.getCollectedMetric().getParameters()) {
            dc.addParameter(parameter.getName(), parameter.getValue());
        }
        for (MonitoredTarget target : rule.getMonitoredTargets().getMonitoredTargets()) {
            dc.addTargetResource(target.getClazz(), target.getType(), target.getId());
        }
        try {
            dc.setDaUrl(config.getDaUrl() + "/streams/" + URLEncoder.encode(prepareStreamURI(metric), "UTF-8"));
            dc.setDataFormat("TOWER/JSON");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        return dc;
    }

    private String prepareStreamURI(String metric) {
        return "http://www.modaclouds.eu/streams/" + metric;
    }

    private String prepareQueryURI(String queryId) {
        return config.getDaUrl() + "/queries/" + queryId;
    }

    private void validate(MonitoringRules rules) throws RuleInstallationException {
        Set<Problem> problems = new HashSet<Problem>();
        List<MonitoringRule> otherRules = new ArrayList<MonitoringRule>(rulesByRuleId.values());
        otherRules.addAll(rules.getMonitoringRules());
        MonitoringRule previousRule = null;
        for (MonitoringRule rule : rules.getMonitoringRules()) {
            if (previousRule != null)
                otherRules.add(previousRule);
            otherRules.remove(rule);
            problems.addAll(validator.validateRule(rule, otherRules));
            previousRule = rule;
        }
        if (!problems.isEmpty()) {
            String message = "Rules could not be installed. Problems:";
            for (Problem p : problems) {
                message += " [Rule " + p.getId() + ", error: " + p.getError() + " at position " + p.getTagName()
                        + (p.getDescription() != null ? ", details: " + p.getDescription() : "") + "]";
            }
            throw new RuleInstallationException(message);
        }
    }

    @Override
    public synchronized void uninstallRule(String ruleId) throws NotFoundException, IOException {
        logger.info("Uninstalling rule {}", ruleId);
        if (!rulesByRuleId.containsKey(ruleId)) {
            throw new NotFoundException(ruleId);
        }
        cleanUpRule(ruleId);
        rulesByRuleId.remove(ruleId);
    }

    private void cleanUpRule(String ruleId) throws IOException {
        String queryId = queryIdByRuleId.get(ruleId);
        if (queryId == null)
            return;
        try {
            if (queryByQueryId.containsKey(queryId)) {
                String queryURI = prepareQueryURI(queryId);
                logger.debug("Uninstalling query {}", queryId);
                dataAnalyzer.unregisterQuery(queryURI);
                Query query = queryByQueryId.get(queryId);
                if (query.hasOutputMetric()) {
                    ruleIdByObservableMetric.remove(query.getOutputMetric());
                    streamsByMetric.remove(query.getOutputMetric());
                    Set<String> observers = observersIdsByMetric.remove(query.getOutputMetric());
                    if (observers != null) {
                        for (String obsId : observers) {
                            observersById.remove(obsId);
                        }
                    }
                }
                queryByQueryId.remove(queryId);
            }
            Set<String> inputMetrics = inputMetricsByRuleId.get(ruleId);
            if (inputMetrics != null) {
                for (String metric : inputMetrics) {
                    rulesIdsByInputMetric.get(metric).remove(ruleId);
                    if (rulesIdsByInputMetric.get(metric).isEmpty())
                        rulesIdsByInputMetric.remove(metric);
                    if (!ruleIdByObservableMetric.containsKey(metric)
                            && rulesIdsByInputMetric.get(metric) == null) {
                        if (streamsByMetric.containsKey(metric)) {
                            dataAnalyzer.unregisterStream(prepareStreamURI(metric));
                            streamsByMetric.remove(metric);
                        }
                    }
                }
                inputMetricsByRuleId.remove(ruleId);
                synchronized (dcAndResourcesLock) {
                    dCConfigByRuleId.remove(ruleId);
                }
            }
        } catch (ServerErrorException e) {
            throw new IOException(e);
        } catch (QueryErrorException | StreamErrorException e) {
            throw new RuntimeException(e);
        }
        queryIdByRuleId.remove(ruleId);
    }

    @Override
    public void enableRule(String id) throws NotFoundException, IOException {
        // TODO using startEnabled field, should be renamed to enabled
        logger.info("Enabling rule {}", id);
        MonitoringRule rule = rulesByRuleId.get(id);
        if (rule == null)
            throw new NotFoundException(id);
        if (rule.isStartEnabled())
            return;
        String queryId = queryIdByRuleId.get(id);
        if (queryId == null) {
            throw new RuntimeException("No query found related to installed rule " + id);
        }
        String queryURI = prepareQueryURI(queryId);
        logger.debug("Enabling query {}", queryURI);
        try {
            dataAnalyzer.restartQuery(queryURI);
        } catch (Exception e) {
            throw new IOException(e);
        }
        rule.setStartEnabled(true);
    }

    @Override
    public void disableRule(String id) throws NotFoundException, IOException {
        logger.info("Disabling rule {}", id);
        MonitoringRule rule = rulesByRuleId.get(id);
        if (rule == null)
            throw new NotFoundException(id);
        if (!rule.isStartEnabled())
            return;
        String queryId = queryIdByRuleId.get(id);
        if (queryId == null) {
            throw new RuntimeException("No query found related to installed rule " + id);
        }
        String queryURI = prepareQueryURI(queryId);
        logger.debug("Disabling query {}", queryURI);
        try {
            dataAnalyzer.pauseQuery(queryURI);
        } catch (Exception e) {
            throw new IOException(e);
        }
        rule.setStartEnabled(false);
    }

    // private boolean noOtherRuleIsUsingIt(String ruleId, String metric) {
    // HashMap<String, Set<String>> copy = new HashMap<String, Set<String>>(
    // inputMetricsByRuleId);
    // copy.remove(ruleId);
    // return !copy.containsValue(metric);
    // }

    // public synchronized DCConfiguration getDCConfig(String metric) {
    // dcLock.lock();
    // DCConfiguration dcConfiguration;
    // try {
    // dcConfiguration = dCConfigByMetric.get(metric);
    // } finally {
    // dcLock.unlock();
    // }
    // return dcConfiguration;
    // }

    public synchronized MonitoringRule getMonitoringRule(String ruleId) throws NotFoundException {
        if (!rulesByRuleId.containsKey(ruleId)) {
            throw new NotFoundException(ruleId);
        }
        return rulesByRuleId.get(ruleId);
    }

    public synchronized Set<String> getObservableMetrics() {
        return ruleIdByObservableMetric.keySet();
    }

    @Override
    public synchronized MonitoringRules getRules() {
        MonitoringRules rules = new MonitoringRules();
        rules.getMonitoringRules().addAll(rulesByRuleId.values());
        return rules;
    }

    @Override
    public synchronized Observer registerHttpObserver(String metric, String callbackUrl, String format)
            throws NotFoundException, IOException {
        logger.info("Adding http observer with callbackURL {} to metric {} using format {}", callbackUrl, metric,
                format);
        if (!ruleIdByObservableMetric.containsKey(metric)) {
            throw new NotFoundException(metric);
        }
        String ruleId = ruleIdByObservableMetric.get(metric);
        String queryUri = prepareQueryURI(queryIdByRuleId.get(ruleId));
        String returnedObserverUri;
        try {
            returnedObserverUri = dataAnalyzer.addHttpObserver(queryUri, new URL(callbackUrl).toString(), format);
        } catch (ServerErrorException e) {
            throw new IOException();
        } catch (ObserverErrorException e) {
            throw new RuntimeException();
        }
        String observerId = returnedObserverUri.substring(returnedObserverUri.lastIndexOf("/") + 1);

        Observer observer = observersById.get(observerId);
        if (observer == null) {
            observer = new Observer(observerId, callbackUrl, "HTTP", format);
            observersById.put(observerId, observer);
        }
        observer.addObservedQueryUri(queryUri);

        Set<String> observersIds = observersIdsByMetric.get(metric);
        if (observersIds == null) {
            observersIds = new HashSet<String>();
            observersIdsByMetric.put(metric, observersIds);
        }
        observersIds.add(observer.getId());

        return observer;
    }

    @Override
    public synchronized Observer registerSocketObserver(String metric, String observerHost, int observerPort,
            SocketProtocol protocol, String format) throws NotFoundException, IOException {
        logger.info("Adding socket observer with host {} and port {} to metric {} using protocol {} and format {}",
                observerHost, observerPort, metric, protocol, format);
        if (!ruleIdByObservableMetric.containsKey(metric)) {
            throw new NotFoundException(metric);
        }
        String ruleId = ruleIdByObservableMetric.get(metric);
        String queryUri = prepareQueryURI(queryIdByRuleId.get(ruleId));
        String returnedObserverUri;
        try {
            returnedObserverUri = dataAnalyzer.addSocketObserver(queryUri, observerHost, observerPort,
                    protocol.name(), format);
        } catch (ServerErrorException e) {
            throw new IOException();
        } catch (ObserverErrorException e) {
            throw new RuntimeException(e);
        }
        String observerId = returnedObserverUri.substring(returnedObserverUri.lastIndexOf("/") + 1);

        Observer observer = observersById.get(observerId);
        if (observer == null) {
            observer = new Observer(observerId, observerHost, observerPort, protocol.name(), format);
            observersById.put(observerId, observer);
        }
        observer.addObservedQueryUri(queryUri);

        Set<String> observersIds = observersIdsByMetric.get(metric);
        if (observersIds == null) {
            observersIds = new HashSet<String>();
            observersIdsByMetric.put(metric, observersIds);
        }
        observersIds.add(observer.getId());

        return observer;
    }

    public synchronized Set<Observer> getObservers(String metric) throws NotFoundException {
        if (!ruleIdByObservableMetric.containsKey(metric)) {
            throw new NotFoundException(metric);
        }
        Set<String> observersIds = observersIdsByMetric.get(metric);
        HashSet<Observer> observers = new HashSet<Observer>();
        if (observersIds != null) {
            for (String id : observersIds) {
                observers.add(observersById.get(id));
            }
        }
        return observers;
    }

    public synchronized void unregisterObserver(String metric, String observerId)
            throws NotFoundException, IOException {
        logger.info("Removing observer {} from metric {}", observerId, metric);
        if (!ruleIdByObservableMetric.containsKey(metric)) {
            throw new NotFoundException(metric);
        }
        if (!isMetricObservedBy(metric, observerId)) {
            throw new NotFoundException(observerId);
        }
        try {
            String ruleId = ruleIdByObservableMetric.get(metric);
            String queryId = queryIdByRuleId.get(ruleId);
            String queryUri = prepareQueryURI(queryId);
            dataAnalyzer.deleteObserver(queryUri + "/observers/" + observerId);
            Observer observer = observersById.get(observerId);
            Set<String> observerdQueriesUris = observer.getObserverdQueriesUris();
            observerdQueriesUris.remove(queryUri);
            if (observerdQueriesUris.isEmpty())
                observersById.remove(observerId);
            observersIdsByMetric.get(metric).remove(observerId);
        } catch (ServerErrorException e) {
            throw new IOException();
        } catch (ObserverErrorException e) {
            throw new RuntimeException(e);
        }
    }

    private boolean isMetricObservedBy(String metric, String observerId) {
        return observersIdsByMetric.get(metric) != null && observersIdsByMetric.get(metric).contains(observerId);
    }

    @Override
    public void deleteResource(String resourceId) throws IOException, NotFoundException {
        logger.info("Deleting resource {} from KB", resourceId);
        synchronized (dcAndResourcesLock) {
            if (!registeredResources.containsKey(resourceId)) {
                throw new NotFoundException(resourceId);
            }
            knowledgeBase.deleteEntitiesByPropertyValue(resourceId, MOVocabulary.idParameterName,
                    ManagerConfig.MODEL_GRAPH_NAME);
            registeredResources.remove(resourceId);
            resourcesKeepAlive.remove(resourceId);
            resourcesKATimestamp.remove(resourceId);
        }
        if (rdfHistoryDB != null) {
            try {
                logger.info("Sending udpate to RDF History DB");
                rdfHistoryDB.deleteResource(resourceId);
            } catch (Exception e) {
                logger.error("Error while sending udpate to RDF History DB: {}", e.getMessage());
            }
        }
    }

    public void replaceResources(Set<Resource> resources) throws IOException, SerializationException {
        logger.info("Clearing existing model if any");
        synchronized (dcAndResourcesLock) {
            knowledgeBase.clearGraph(ManagerConfig.MODEL_GRAPH_NAME);
            registeredResources.clear();
            resourcesKeepAlive.clear();
            resourcesKATimestamp.clear();
            long timestamp = System.currentTimeMillis();
            logger.info("Registering {} new resources", resources.size());
            knowledgeBase.addMany(resources, MOVocabulary.idParameterName, ManagerConfig.MODEL_GRAPH_NAME);
            for (Resource resource : resources) {
                registeredResources.put(resource.getId(), resource);
                resourcesKATimestamp.put(resource.getId(), timestamp);
                resourcesKeepAlive.put(resource.getId(), MAX_KEEP_ALIVE);
            }
        }
        if (rdfHistoryDB != null) {
            try {
                logger.info("Sending udpate to RDF History DB");
                rdfHistoryDB.replaceResources(resources);
            } catch (Exception e) {
                logger.error("Error while sending udpate to RDF History DB: {}", e.getMessage());
            }
        }
    }

    public Resource getResource(String id) throws NotFoundException {
        synchronized (dcAndResourcesLock) {
            if (!registeredResources.containsKey(id))
                throw new NotFoundException(id);
            return registeredResources.get(id);
        }
    }

    @Override
    public Collection<Resource> getResources() throws DeserializationException, IOException {
        synchronized (dcAndResourcesLock) {
            return registeredResources.values();
        }
    }

    @Override
    public String registerDataCollector(DCDescriptor dCDescriptor) throws SerializationException, IOException {
        String dcId = UUID.randomUUID().toString();
        registerDataCollector(dcId, dCDescriptor);
        return dcId;
    }

    public void addResources(Set<Resource> resources) throws SerializationException, IOException {
        synchronized (dcAndResourcesLock) {
            long timestamp = System.currentTimeMillis();
            logger.info("Registering {} new resources", resources.size());
            updateExistingRelations(resources);
            knowledgeBase.addMany(resources, MOVocabulary.idParameterName, ManagerConfig.MODEL_GRAPH_NAME);
            for (Resource resource : resources) {
                registeredResources.put(resource.getId(), resource);
                resourcesKATimestamp.put(resource.getId(), timestamp);
                resourcesKeepAlive.put(resource.getId(), MAX_KEEP_ALIVE);
            }
        }
        if (rdfHistoryDB != null) {
            try {
                logger.info("Sending udpate to RDF History DB");
                rdfHistoryDB.addResources(resources);
            } catch (Exception e) {
                logger.error("Error while sending udpate to RDF History DB: {}", e.getMessage());
            }
        }
    }

    @SuppressWarnings("unchecked")
    private void updateExistingRelations(Set<Resource> resources) {
        try {
            Map<String, Object> properties;
            for (Resource resource : resources) {
                if (registeredResources.containsKey(resource.getId())) {
                    Resource registeredResource = registeredResources.get(resource.getId());
                    properties = PropertyUtils.describe(resource);
                    for (Entry<String, Object> property : properties.entrySet()) {
                        if (property.getValue() instanceof Set) {
                            ((Set<Resource>) property.getValue()).addAll((Set<? extends Resource>) PropertyUtils
                                    .getProperty(registeredResource, property.getKey()));
                        }
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // @Monitor(type = "registerDataCollector")
    public void registerDataCollector(String dcId, DCDescriptor dCDescriptor)
            throws SerializationException, IOException {
        // TODO validate dc descriptor!!!
        Set<Resource> resources;
        synchronized (dcAndResourcesLock) {
            long timestamp = System.currentTimeMillis();
            logger.debug("Registering DC: {}", dCDescriptor);
            resources = dCDescriptor.getResources();
            if (resources != null && !resources.isEmpty()) {
                updateExistingRelations(resources);
                logger.info("Adding {} to KB", resources);
                knowledgeBase.addMany(resources, MOVocabulary.idParameterName, ManagerConfig.MODEL_GRAPH_NAME);
                logger.info("{} added to KB", resources);
            }
            registeredDCs.put(dcId, dCDescriptor);
            int keepAlive = dCDescriptor.getKeepAlive();
            if (keepAlive <= 0) {
                keepAlive = MAX_KEEP_ALIVE;
            }
            dcsKeepAlive.put(dcId, keepAlive);
            dcsKATimestamp.put(dcId, timestamp);
            for (Resource resource : resources) {
                registeredResources.put(resource.getId(), resource);
                Integer currentKeepAlive = resourcesKeepAlive.get(resource.getId());
                if (currentKeepAlive == null || currentKeepAlive < keepAlive) {
                    resourcesKeepAlive.put(resource.getId(), keepAlive);
                }
                resourcesKATimestamp.put(resource.getId(), timestamp);
            }
        }
        if (rdfHistoryDB != null) {
            try {
                logger.info("Sending udpate to RDF History DB");
                rdfHistoryDB.addResources(resources);
            } catch (Exception e) {
                logger.error("Error while sending udpate to RDF History DB: {}", e.getMessage());
            }
        }
    }

    @Override
    public void unregisterDataCollector(String dcId) throws NotFoundException {
        synchronized (dcAndResourcesLock) {
            DCDescriptor dCDescriptor = registeredDCs.get(dcId);
            if (dCDescriptor == null) {
                throw new NotFoundException(dcId);
            }
            logger.debug("Unregistering DC: {}", dcId);
            registeredDCs.remove(dcId);
            dcsKeepAlive.remove(dcId);
            dcsKATimestamp.remove(dcId);
        }
    }

    // public void keepAlive(String dcId) throws NotFoundException,
    // SerializationException, IOException {
    // dcLock.lock();
    // try {
    // DCDescriptor dCDescriptor = registeredDCs.get(dcId);
    // if (dCDescriptor == null) {
    // throw new NotFoundException(dcId);
    // }
    // logger.info("Keep alive requested by DC {}", dcId);
    // Set<Resource> resources = dCDescriptor.getMonitoredResources();
    // int keepAlive = dCDescriptor.getKeepAlive();
    // if (keepAlive <= 0) {
    // keepAlive = Integer.MAX_VALUE;
    // }
    // for (Resource resource : resources) {
    // if (!registeredResources.containsKey(resource.getId())) {
    // knowledgeBase.add(resource, MOVocabulary.idParameterName,
    // ManagerConfig.MODEL_GRAPH_NAME);
    // registeredResources.put(resource.getId(), resource);
    // resourcesKeepAlive.put(resource.getId(), keepAlive);
    // } else {
    // int newKeepAlive = Math.max(keepAlive,
    // resourcesKeepAlive.get(resource.getId()));
    // resourcesKeepAlive.put(resource.getId(), newKeepAlive);
    // }
    // }
    // dcsKeepAlive.put(dcId, keepAlive);
    // } finally {
    // dcLock.unlock();
    // }
    // }

    // @Monitor(type = "keepAlive")
    @Override
    public void keepAlive(String dcId) throws NotFoundException, SerializationException, IOException {
        synchronized (dcAndResourcesLock) {
            long timestamp = System.currentTimeMillis();
            DCDescriptor dCDescriptor = registeredDCs.get(dcId);
            if (dCDescriptor == null) {
                throw new NotFoundException(dcId);
            }
            logger.debug("Keep alive requested by DC {}", dcId);
            Set<Resource> resources = dCDescriptor.getResources();
            int keepAlive = dCDescriptor.getKeepAlive();
            if (keepAlive <= 0) {
                keepAlive = MAX_KEEP_ALIVE;
            }
            for (Resource resource : resources) {
                if (!registeredResources.containsKey(resource.getId())) {
                    logger.info("Restoring resource {}", resource.getId());
                    knowledgeBase.add(resource, MOVocabulary.idParameterName, ManagerConfig.MODEL_GRAPH_NAME);
                    registeredResources.put(resource.getId(), resource);
                    resourcesKeepAlive.put(resource.getId(), keepAlive);
                    if (rdfHistoryDB != null) {
                        try {
                            logger.info("Sending udpate to RDF History DB");
                            rdfHistoryDB.addResource(resource);
                        } catch (Exception e) {
                            logger.error("Error while sending udpate to RDF History DB: {}", e.getMessage());
                        }
                    }
                }
                resourcesKATimestamp.put(resource.getId(), timestamp);
            }
            dcsKATimestamp.put(dcId, timestamp);
        }
    }

    @Override
    public Map<String, DCDescriptor> getRegisteredDataCollectors() {
        synchronized (dcAndResourcesLock) {
            return registeredDCs;
        }
    }

    public DCDescriptor getRegisteredDC(String id) throws NotFoundException {
        DCDescriptor dc;
        synchronized (dcAndResourcesLock) {
            dc = registeredDCs.get(id);
            if (dc == null) {
                throw new NotFoundException(id);
            }
        }
        return dc;
    }

    // @Monitor(type = "getDCConfigurationByMetric")
    @Override
    public Map<String, Set<DCConfiguration>> getDCConfigurationsByMetric(String dcId)
            throws NotFoundException, SerializationException, IOException {
        Map<String, Set<DCConfiguration>> configs = new HashMap<String, Set<DCConfiguration>>();
        synchronized (dcAndResourcesLock) {
            DCDescriptor dc = registeredDCs.get(dcId);
            if (dc == null) {
                throw new NotFoundException(dcId);
            }
            logger.debug("Configuration requested by DC {}, launching keep alive first", dcId);
            keepAlive(dcId);
            String metric;
            Set<Resource> resources;
            DCConfiguration conf;
            for (Entry<String, Set<Resource>> entry : dc.getMonitoredResourcesByMetric().entrySet()) {
                metric = entry.getKey();
                resources = entry.getValue();
                Set<String> rulesIds = rulesIdsByInputMetric.get(metric);
                if (rulesIds != null) {
                    for (String ruleId : rulesIds) {
                        conf = dCConfigByRuleId.get(ruleId);
                        if (conf != null) {
                            for (Resource resource : resources) {
                                if (conf.isAboutResource(resource)) {
                                    Set<DCConfiguration> dcconfigs = configs.get(metric);
                                    if (dcconfigs == null) {
                                        dcconfigs = new HashSet<DCConfiguration>();
                                        configs.put(metric, dcconfigs);
                                    }
                                    dcconfigs.add(conf);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
        return configs;
    }

    // public class keepAliveChecker implements Runnable {
    //
    // @Override
    // public void run() {
    // dcLock.lock();
    // try {
    // logger.info("Keep alive checker running");
    // for (String dcId : registeredDCs.keySet()) {
    // int newKeepAlive = dcsKeepAlive.get(dcId)
    // - keepAliveCheckPeriod;
    // if (newKeepAlive < 0) {
    // logger.info("DC {} expired, removing it", dcId);
    // dcsKeepAlive.remove(dcId);
    // registeredDCs.remove(dcId);
    // } else {
    // dcsKeepAlive.put(dcId, newKeepAlive);
    // for (Resource resource : registeredDCs.get(dcId)
    // .getMonitoredResources()) {
    // if (!registeredResources.containsKey(resource
    // .getId())) {
    // try {
    // logger.info("Restoring resource {}",
    // resource.getId());
    // knowledgeBase.add(resource,
    // MOVocabulary.idParameterName,
    // ManagerConfig.MODEL_GRAPH_NAME);
    // registeredResources.put(resource.getId(),
    // resource);
    // resourcesKeepAlive.put(resource.getId(),
    // newKeepAlive);
    // } catch (Exception e) {
    // logger.error(
    // "Could not register resource {}",
    // resource.getId(), e);
    // }
    // }
    // }
    // }
    // }
    // Set<String> resourcesIdsToRemove = new HashSet<String>();
    // for (String resourceId : registeredResources.keySet()) {
    // int newValue = resourcesKeepAlive.get(resourceId)
    // - keepAliveCheckPeriod;
    // if (newValue < 0) {
    // resourcesIdsToRemove.add(resourceId);
    // } else {
    // resourcesKeepAlive.put(resourceId, newValue);
    // }
    // }
    // try {
    // if (!resourcesIdsToRemove.isEmpty()) {
    // logger.info("Resources {} expired, removing them",
    // resourcesIdsToRemove);
    // knowledgeBase.deleteEntitiesByPropertyValues(
    // resourcesIdsToRemove,
    // MOVocabulary.idParameterName,
    // ManagerConfig.MODEL_GRAPH_NAME);
    // for (String resourceId : resourcesIdsToRemove) {
    // resourcesKeepAlive.remove(resourceId);
    // registeredResources.remove(resourceId);
    // }
    // }
    // } catch (Exception e) {
    // logger.error("Could not delete expired resources from kb",
    // e);
    // }
    // } finally {
    // dcLock.unlock();
    // }
    // }
    // }

    public class keepAliveChecker implements Runnable {

        @Override
        public void run() {
            Set<String> resourcesIdsToRemove = new HashSet<String>();
            synchronized (dcAndResourcesLock) {
                long timestamp = System.currentTimeMillis();
                logger.info("Checking for expired resources");
                for (String dcId : registeredDCs.keySet()) {
                    if (timestamp - dcsKATimestamp.get(dcId) > dcsKeepAlive.get(dcId) * 1000) {
                        logger.info("DC {} expired, removing it", dcId);
                        dcsKeepAlive.remove(dcId);
                        dcsKATimestamp.remove(dcId);
                        registeredDCs.remove(dcId);
                    } else {
                        for (Resource resource : registeredDCs.get(dcId).getResources()) {
                            if (!registeredResources.containsKey(resource.getId())) {
                                try {
                                    logger.info("Restoring resource {}", resource.getId());
                                    knowledgeBase.add(resource, MOVocabulary.idParameterName,
                                            ManagerConfig.MODEL_GRAPH_NAME);
                                    registeredResources.put(resource.getId(), resource);
                                    resourcesKeepAlive.put(resource.getId(), dcsKeepAlive.get(dcId));
                                    resourcesKATimestamp.put(resource.getId(), dcsKATimestamp.get(dcId));
                                    if (rdfHistoryDB != null) {
                                        rdfHistoryDB.addResource(resource);
                                    }
                                } catch (Exception e) {
                                    logger.error("Could not register resource {}", resource.getId(), e);
                                }
                            }
                        }
                    }
                }
                for (String resourceId : registeredResources.keySet()) {
                    if (timestamp - resourcesKATimestamp.get(resourceId) > resourcesKeepAlive.get(resourceId)
                            * 1000) {
                        resourcesIdsToRemove.add(resourceId);
                    }
                }
                try {
                    if (!resourcesIdsToRemove.isEmpty()) {
                        logger.info("Resources {} expired, removing them", resourcesIdsToRemove);
                        knowledgeBase.deleteEntitiesByPropertyValues(resourcesIdsToRemove,
                                MOVocabulary.idParameterName, ManagerConfig.MODEL_GRAPH_NAME);
                        logger.info("Resources {} removed", resourcesIdsToRemove);
                        for (String resourceId : resourcesIdsToRemove) {
                            resourcesKeepAlive.remove(resourceId);
                            resourcesKATimestamp.remove(resourceId);
                            registeredResources.remove(resourceId);
                        }
                    }
                } catch (Exception e) {
                    logger.error("Could not delete expired resources from kb", e);
                }
            }
            if (rdfHistoryDB != null && !resourcesIdsToRemove.isEmpty()) {
                try {
                    logger.info("Sending udpate to RDF History DB");
                    rdfHistoryDB.deleteResources(resourcesIdsToRemove);
                } catch (Exception e) {
                    logger.error("Error while sending udpate to RDF History DB: {}", e.getMessage());
                }
            }
        }
    }

    // @Monitor(type = "executeAction")
    public void executeAction(String ruleId, String resourceId, String value, String timestamp) {
        logger.debug("Action requested for rule {}, with resourceId {}, value {} and timestamp {}", ruleId,
                resourceId, value, timestamp);
        String queryId = queryIdByRuleId.get(ruleId);
        if (queryId == null) {
            logger.error("No query associated to rule {}", ruleId);
            return;
        }
        Query query = queryByQueryId.get(queryId);
        if (query == null) {
            logger.error("Query {} is not installed", queryId);
            return;
        }
        if (query.hasActions()) {
            List<AbstractAction> actions = query.getActions();
            for (AbstractAction action : actions) {
                actionsExecutor.submit(new ActionRequest(action, resourceId, value, timestamp));
            }
        }
    }

    public class ActionRequest implements Runnable {

        private AbstractAction action;
        private String value;
        private String timestamp;
        private String resourceId;

        public ActionRequest(AbstractAction action, String resourceId, String value, String timestamp) {
            this.action = action;
            this.resourceId = resourceId;
            this.value = value;
            this.timestamp = timestamp;
        }

        @Override
        public void run() {
            action.execute(resourceId, value, timestamp);
        }
    }

    /**
     * 
     * @return the metrics required by one or more installed rule that are not
     *         provided as output metric of other rules
     */
    @Override
    public Set<String> getRequiredMetrics() {
        HashSet<String> requiredMetrics = new HashSet<String>(streamsByMetric.keySet());
        requiredMetrics.removeAll(ruleIdByObservableMetric.keySet());
        return requiredMetrics;
    }

}