org.openhab.core.drools.internal.RuleService.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.core.drools.internal.RuleService.java

Source

/**
 * Copyright (c) 2010-2014, openHAB.org and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.openhab.core.drools.internal;

import static org.openhab.core.events.EventConstants.TOPIC_PREFIX;
import static org.openhab.core.events.EventConstants.TOPIC_SEPERATOR;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.ObjectFilter;
import org.drools.SystemEventListener;
import org.drools.SystemEventListenerFactory;
import org.drools.agent.KnowledgeAgent;
import org.drools.agent.KnowledgeAgentConfiguration;
import org.drools.agent.KnowledgeAgentFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.io.ResourceChangeScannerConfiguration;
import org.drools.io.ResourceFactory;
import org.drools.runtime.StatefulKnowledgeSession;
import org.drools.runtime.rule.FactHandle;
import org.openhab.core.drools.event.CommandEvent;
import org.openhab.core.drools.event.RuleEvent;
import org.openhab.core.drools.event.StateEvent;
import org.openhab.core.items.GenericItem;
import org.openhab.core.items.Item;
import org.openhab.core.items.ItemNotFoundException;
import org.openhab.core.items.ItemRegistry;
import org.openhab.core.items.ItemRegistryChangeListener;
import org.openhab.core.items.StateChangeListener;
import org.openhab.core.service.AbstractActiveService;
import org.openhab.core.types.Command;
import org.openhab.core.types.EventType;
import org.openhab.core.types.State;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RuleService extends AbstractActiveService
        implements ManagedService, EventHandler, ItemRegistryChangeListener, StateChangeListener {

    private static final String RULES_CHANGESET = "org/openhab/core/drools/changeset.xml";

    static private final Logger logger = LoggerFactory.getLogger(RuleService.class);

    private ItemRegistry itemRegistry = null;

    private long refreshInterval = 200;

    private StatefulKnowledgeSession ksession = null;

    private Map<String, FactHandle> factHandleMap = new HashMap<String, FactHandle>();

    private List<RuleEvent> eventQueue = Collections.synchronizedList(new ArrayList<RuleEvent>());

    public void activate() {

        SystemEventListenerFactory.setSystemEventListener(new RuleEventListener());

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add(ResourceFactory.newClassPathResource(RULES_CHANGESET, getClass()), ResourceType.CHANGE_SET);

        if (kbuilder.hasErrors()) {
            logger.error("There are errors in the rules: " + kbuilder.getErrors());
            return;
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();

        KnowledgeAgentConfiguration aconf = KnowledgeAgentFactory.newKnowledgeAgentConfiguration();
        aconf.setProperty("drools.agent.newInstance", "false");
        KnowledgeAgent kagent = KnowledgeAgentFactory.newKnowledgeAgent("RuleAgent", kbase, aconf);
        kagent.applyChangeSet(ResourceFactory.newClassPathResource(RULES_CHANGESET, getClass()));
        kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
        ksession = kbase.newStatefulKnowledgeSession();

        // activate notifications
        ResourceFactory.getResourceChangeNotifierService().start();
        ResourceFactory.getResourceChangeScannerService().start();

        // activate this for extensive logging
        // KnowledgeRuntimeLoggerFactory.newConsoleLogger(ksession);

        // set the scan interval to 20 secs
        ResourceChangeScannerConfiguration sconf = ResourceFactory.getResourceChangeScannerService()
                .newResourceChangeScannerConfiguration();
        sconf.setProperty("drools.resource.scanner.interval", "20");
        ResourceFactory.getResourceChangeScannerService().configure(sconf);

        // now add all registered items to the session
        if (itemRegistry != null) {
            for (Item item : itemRegistry.getItems()) {
                itemAdded(item);
            }
        }

        setProperlyConfigured(true);
    }

    public void deactivate() {
        if (ksession != null) {
            ksession.dispose();
            ksession = null;
        }
        factHandleMap.clear();
        shutdown();
    }

    public void setItemRegistry(ItemRegistry itemRegistry) {
        this.itemRegistry = itemRegistry;
        itemRegistry.addItemRegistryChangeListener(this);
    }

    public void unsetItemRegistry(ItemRegistry itemRegistry) {
        itemRegistry.removeItemRegistryChangeListener(this);
        this.itemRegistry = null;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("rawtypes")
    public void updated(Dictionary config) throws ConfigurationException {
        if (config != null) {
            String evalIntervalString = (String) config.get("evalInterval");
            if (StringUtils.isNotBlank(evalIntervalString)) {
                refreshInterval = Long.parseLong(evalIntervalString);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public void allItemsChanged(Collection<String> oldItemNames) {
        if (ksession != null) {
            // first remove all previous items from the session
            for (String oldItemName : oldItemNames) {
                internalItemRemoved(oldItemName);
            }

            // then add the current ones again
            Collection<Item> items = itemRegistry.getItems();
            for (Item item : items) {
                internalItemAdded(item);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public void itemAdded(Item item) {
        if (ksession != null) {
            internalItemAdded(item);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void itemRemoved(Item item) {
        if (ksession != null) {
            internalItemRemoved(item.getName());
            if (item instanceof GenericItem) {
                GenericItem genericItem = (GenericItem) item;
                genericItem.removeStateChangeListener(this);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public void stateChanged(Item item, State oldState, State newState) {
        eventQueue.add(new StateEvent(item, oldState, newState));
    }

    /**
     * {@inheritDoc}
     */
    public void stateUpdated(Item item, State state) {
        eventQueue.add(new StateEvent(item, state));
    }

    public void receiveCommand(String itemName, Command command) {
        try {
            Item item = itemRegistry.getItem(itemName);
            eventQueue.add(new CommandEvent(item, command));
        } catch (ItemNotFoundException e) {
        }
    }

    private void internalItemAdded(Item item) {
        if (item == null) {
            logger.debug("Item must not be null here!");
            return;
        }

        FactHandle handle = factHandleMap.get(item.getName());
        if (handle != null) {
            // we already know this item
            try {
                ksession.update(handle, item);
            } catch (NullPointerException e) {
                // this can be thrown because of a bug in drools when closing down the system
            }
        } else {
            // it is a new item
            handle = ksession.insert(item);
            factHandleMap.put(item.getName(), handle);
            if (item instanceof GenericItem) {
                GenericItem genericItem = (GenericItem) item;
                genericItem.addStateChangeListener(this);
            }
        }
    }

    private void internalItemRemoved(String itemName) {
        FactHandle handle = factHandleMap.get(itemName);
        if (handle != null) {
            factHandleMap.remove(itemName);
            ksession.retract(handle);
        }
    }

    /**
     * @{inheritDoc}
     */
    @Override
    protected synchronized void execute() {
        // remove all previous events from the session
        Collection<FactHandle> handles = ksession.getFactHandles(new ObjectFilter() {
            public boolean accept(Object obj) {
                if (obj instanceof RuleEvent) {
                    return true;
                }
                return false;
            }
        });
        for (FactHandle handle : handles) {
            ksession.retract(handle);
        }

        ArrayList<RuleEvent> clonedQueue = new ArrayList<RuleEvent>(eventQueue);
        eventQueue.clear();

        // now add all recent events to the session
        for (RuleEvent event : clonedQueue) {
            Item item = event.getItem();
            if (ksession != null && item != null) {
                FactHandle factHandle = factHandleMap.get(item.getName());
                if (factHandle != null) {
                    ksession.update(factHandle, item);
                }
                ksession.insert(event);
            }
        }

        // run the rule evaluation
        ksession.fireAllRules();

    }

    @Override
    protected long getRefreshInterval() {
        return refreshInterval;
    }

    @Override
    protected String getName() {
        return "Rule Evaluation Service";
    }

    /**
     * {@inheritDoc}
     */
    public void handleEvent(Event event) {
        String itemName = (String) event.getProperty("item");

        String topic = event.getTopic();
        String[] topicParts = topic.split(TOPIC_SEPERATOR);

        if (!(topicParts.length > 2) || !topicParts[0].equals(TOPIC_PREFIX)) {
            return; // we have received an event with an invalid topic
        }
        String operation = topicParts[1];

        if (operation.equals(EventType.COMMAND.toString())) {
            Command command = (Command) event.getProperty("command");
            if (command != null)
                receiveCommand(itemName, command);
        }
    }

    static private final class RuleEventListener implements SystemEventListener {

        private final Logger logger = LoggerFactory.getLogger(SystemEventListener.class);

        public void warning(String message, Object object) {
            logger.warn(message);
        }

        public void warning(String message) {
            logger.warn(message);
        }

        public void info(String message, Object object) {
            logger.info(message);
        }

        public void info(String message) {
            logger.info(message);
        }

        public void exception(String message, Throwable e) {
            logger.error(message, e);
        }

        public void exception(Throwable e) {
            logger.error(e.getLocalizedMessage(), e);
        }

        public void debug(String message, Object object) {
            logger.debug(message);
        }

        public void debug(String message) {
            logger.debug(message);
        }
    }

}