All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.freedomotic.api.Protocol Maven / Gradle / Ivy

/**
 *
 * Copyright (c) 2009-2014 Freedomotic team http://freedomotic.com
 *
 * This file is part of Freedomotic
 *
 * This Program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2, or (at your option) any later version.
 *
 * This Program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * Freedomotic; see the file COPYING. If not, see
 * .
 */
package com.freedomotic.api;

import com.freedomotic.exceptions.PluginRuntimeException;
import com.freedomotic.events.PluginHasChanged;
import com.freedomotic.exceptions.PluginShutdownException;
import com.freedomotic.exceptions.PluginStartupException;
import com.freedomotic.exceptions.UnableToExecuteException;
import com.freedomotic.reactions.Command;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.ObjectMessage;

/**
 * Uses a Template Method pattern which allows subclass to define how to perform
 * a command or how to act when a specific event is received.
 */
public abstract class Protocol extends Plugin {

    private static final Logger LOG = Logger.getLogger(Protocol.class.getName());
    private int pollingWaitTime = -1;
    private Protocol.SensorThread sensorThread;
    private volatile Destination lastDestination;

    /**
     *
     * @param pluginName
     * @param manifest
     */
    public Protocol(String pluginName, String manifest) {
        super(pluginName, manifest);
        setStatus(PluginStatus.STOPPED);
    }

    /**
     *
     * @throws com.freedomotic.exceptions.PluginRuntimeException
     */
    protected abstract void onRun() throws PluginRuntimeException;

    /**
     *
     * @param c
     * @throws IOException
     * @throws UnableToExecuteException
     */
    protected abstract void onCommand(Command c) throws IOException, UnableToExecuteException;

    /**
     *
     * @param c
     * @return
     */
    protected abstract boolean canExecute(Command c);

    /**
     *
     * @param event
     */
    protected abstract void onEvent(EventTemplate event);


    /**
     *
     * @param listento
     */
    public void addEventListener(String listento) {
        listener.consumeEventFrom(listento);
    }

    /**
     *
     * @param ev
     */
    public void notifyEvent(EventTemplate ev) {
        if (isAllowedToSend()) {
            notifyEvent(ev, ev.getDefaultDestination());
        }
    }

    public Command notifyCommand(Command command) {
        return getBusService().send(command);
    }

    /**
     *
     * @param ev
     * @param destination
     */
    public void notifyEvent(EventTemplate ev, String destination) {
        if (isAllowedToSend()) {
            LOG.fine("Sensor " + this.getName() + " notify event " + ev.getEventName() + ":" + ev.getPayload().toString());
            getBusService().send(ev, destination);
        }
    }

    /**
     *
     */
    @Override
    public void start() {
        super.start();
        if (isAllowedToStart()) {

            Runnable action = new Runnable() {
                @Override
                public synchronized void run() {
                    try {
                        setStatus(PluginStatus.STARTING);
                        //onStart() is called before the thread because it may have some initialization for the sensor thread
                        try {
                            onStart();
                        } catch (PluginStartupException startupEx) {
                            notifyCriticalError(startupEx.getMessage(), startupEx);
                            return; //stop the plugin startup
                        }
                        sensorThread = new Protocol.SensorThread();
                        sensorThread.start();
                        setStatus(PluginStatus.RUNNING);
                        PluginHasChanged event = new PluginHasChanged(this, getName(), PluginHasChanged.PluginActions.START);
                        getBusService().send(event);
                    } catch (Exception e) {
                        setStatus(PluginStatus.FAILED);
                        setDescription("Plugin start FAILED. see logs for details.");
                        LOG.log(Level.SEVERE, "Plugin " + getName() + " start FAILED: " + e.getLocalizedMessage(), e);
                    }

                }
            };
            getApi().getAuth().pluginExecutePrivileged(this, action);
        }
    }

    /**
     *
     */
    @Override
    public void stop() {
        super.stop();
        if (isRunning()) {
            Runnable action = new Runnable() {
                @Override
                public synchronized void run() {
                    try {
                        setStatus(PluginStatus.STOPPING);
                        try {
                            onStop();
                        } catch (PluginShutdownException shutdownEx) {
                            notifyError(shutdownEx.getMessage());
                        }
                        sensorThread = null;
                        listener.unsubscribeEvents();
                        PluginHasChanged event = new PluginHasChanged(this, getName(), PluginHasChanged.PluginActions.STOP);
                        getBusService().send(event);
                        setStatus(PluginStatus.STOPPED);
                    } catch (Exception e) {
                        setStatus(PluginStatus.FAILED);
                        setDescription("Plugin stop FAILED. see logs for details.");
                        LOG.log(Level.SEVERE, "Error stopping " + getName() + ": " + e.getLocalizedMessage(), e);
                    }
                }
            };
            getApi().getAuth().pluginExecutePrivileged(this, action);
        }
    }

    /**
     *
     * @param wait
     */
    public final void setPollingWait(int wait) {
        pollingWaitTime = wait;
    }

    /**
     *
     * @return
     */
    public int getScheduleRate() {
        return pollingWaitTime;
    }

    private boolean isPollingSensor() {
        if (pollingWaitTime > 0) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public final void onMessage(final ObjectMessage message) {
        if (!isRunning()) {
            notifyError("Plugin '" + getName() + "' receives a command while is not running. Turn on the plugin first");
            return;
        }

        Object payload = null;

        try {
            payload = message.getObject();

            if (payload instanceof Command) {
                final Command command = (Command) payload;
                LOG.config(this.getName() + " receives command " + command.getName()
                        + " with parametes {{" + command.getProperties() + "}}");

                Protocol.ActuatorPerforms task;
                lastDestination = message.getJMSReplyTo();
                task
                        = new Protocol.ActuatorPerforms(command,
                                message.getJMSReplyTo(),
                                message.getJMSCorrelationID());
                task.start();
            } else {
                if (payload instanceof EventTemplate) {
                    final EventTemplate event = (EventTemplate) payload;
                    onEvent(event);
                }
            }
        } catch (JMSException ex) {
            LOG.log(Level.SEVERE, null, ex);

        }
    }

    /**
     *
     * @param command
     * @return
     */
    protected Command send(Command command) {
        return getBusService().send(command);
    }

    /**
     *
     * @param command
     */
    public void reply(Command command) {
        // sends back the command
        final String defaultCorrelationID = "-1";
        getBusService().reply(command, lastDestination, defaultCorrelationID);

    }

    private class ActuatorPerforms extends Thread {

        private final Command command;
        private final Destination reply;
        private final String correlationID;

        ActuatorPerforms(Command c, Destination reply, String correlationID) {
            this.command = c;
            this.reply = reply;
            this.correlationID = correlationID;
            this.setName("freedomotic-protocol-executor");
        }

        @Override
        public void run() {
            try {
                // a command is supposed executed if the plugin doesen't say the contrary
                command.setExecuted(true);
                onCommand(command);
            } catch (IOException ex) {
                LOG.log(Level.SEVERE, null, ex);
                command.setExecuted(false);
            } catch (UnableToExecuteException ex) {
                command.setExecuted(false);
                LOG.info(getName() + " failed to execute command " + command.getName() + ": " + ex.getMessage());
            }

            // automatic-reply-to-command is used when the plugin executes the command in a
            // separate thread. In this cases the onCommand() returns immediately (as execution is forked in a thread)
            // and sometimes this is not the intended behavior. Take a look at the Delayer plugin configuration
            // it has to call reply(...) explicitely
            if ((getConfiguration().getBooleanProperty("automatic-reply-to-commands", true) == true) //default value is true
                    && (command.getReplyTimeout() > 0)) {
                getBusService().reply(command, reply, correlationID); //sends back the command marked as executed or not
            }
        }
    }

    private class SensorThread
            extends Thread {

        @Override
        public void run() {
            try {
                if (isPollingSensor()) {
                    Thread thisThread = Thread.currentThread();
                    while (sensorThread == thisThread) {
                        try {
                            Thread.sleep(pollingWaitTime);
                            synchronized (this) {
                                while (!isRunning() && (sensorThread == thisThread)) {
                                    wait();
                                }
                            }
                        } catch (InterruptedException e) {
                            // TODO do Log?
                        }
                        onRun();
                    }
                } else {
                    if (isRunning()) {
                        onRun();
                    }
                }
            } catch (Exception e) {
                notifyCriticalError(e.getMessage(), e);
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy