org.hubiquitus.hubotsdk.Hubot.java Source code

Java tutorial

Introduction

Here is the source code for org.hubiquitus.hubotsdk.Hubot.java

Source

/*
 * Copyright (c) Novedia Group 2012.
 *
 *     This file is part of Hubiquitus.
 *
 *     Hubiquitus 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 3 of the License, or
 *     (at your option) any later version.
 *
 *     Hubiquitus 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 Hubiquitus.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.hubiquitus.hubotsdk;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.impl.JndiRegistry;
import org.hubiquitus.hapi.client.HClient;
import org.hubiquitus.hapi.client.HMessageDelegate;
import org.hubiquitus.hapi.client.HStatusDelegate;
import org.hubiquitus.hapi.exceptions.MissingAttrException;
import org.hubiquitus.hapi.hStructures.ConnectionStatus;
import org.hubiquitus.hapi.hStructures.HAckValue;
import org.hubiquitus.hapi.hStructures.HMessage;
import org.hubiquitus.hapi.hStructures.HMessageOptions;
import org.hubiquitus.hapi.hStructures.HOptions;
import org.hubiquitus.hapi.hStructures.HStatus;
import org.hubiquitus.hapi.hStructures.ResultStatus;
import org.hubiquitus.hubotsdk.adapters.HChannelAdapterInbox;
import org.hubiquitus.hubotsdk.adapters.HubotAdapterInbox;
import org.hubiquitus.hubotsdk.adapters.HubotAdapterOutbox;
import org.hubiquitus.hubotsdk.topology.HAdapterConf;
import org.hubiquitus.hubotsdk.topology.HTopology;
import org.hubiquitus.util.HubotStatus;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class Hubot {

    final Logger logger = LoggerFactory.getLogger(Hubot.class);

    private static final String HUBOT_ADAPTER_OUTBOX = "hubotAdapterOutbox";
    private static final String HUBOT_ADAPTER_INBOX = "hubotAdapterInbox";

    private Hubot outerclass = this;
    private HubotStatus status;

    private HClient hClient = new HClient();
    private DefaultCamelContext camelContext = null;
    private ArrayList<String> adapterOutboxActors = new ArrayList<String>();
    private Map<String, Adapter> adapterInstances = new HashMap<String, Adapter>();
    private HubotDispatcher hubotDispatcher;

    private HTopology topology;

    private MessagesDelegate messageDelegate = new MessagesDelegate();
    private StatusDelegate statusDelegate = new StatusDelegate();

    public static String fileToString(String file) {
        String result = null;
        DataInputStream in = null;

        try {
            File f = new File(file);
            byte[] buffer = new byte[(int) f.length()];
            in = new DataInputStream(new FileInputStream(f));
            in.readFully(buffer);
            result = new String(buffer);
        } catch (IOException e) {
            throw new RuntimeException("IO problem in fileToString", e);
        } finally {
            try {
                in.close();
            } catch (IOException e) { /* ignore it */
            }
        }
        return result;
    }

    /**
     * Connect the Hubot to the hAPI with params set in the file "config.txt"
     */
    protected final void start() {
        try {
            // Create a default context for Camel
            camelContext = new DefaultCamelContext();
            ProducerTemplateSingleton.setContext(camelContext);
            URL configFilePath = ClassLoader.getSystemResource("config.txt");
            String jsonString = fileToString(configFilePath.getFile());
            topology = new HTopology(jsonString);
            setStatus(HubotStatus.CREATED);
            //Connecting to HNode
            HOptions options = new HOptions();
            options.setTransport("socketio");
            JSONArray endpoints = new JSONArray();
            endpoints.put(topology.getHserver());
            options.setEndpoints(endpoints);
            hClient.onStatus(statusDelegate);
            hClient.connect(topology.getActor(), topology.getPwd(), options);
            // see onStatus to know how this actor go the started status
        } catch (Exception e) {
            logger.error("Oooops a big error on starting this bot : ", e);
        }
    }

    /**
     * An Hubot may override this method in order to perform its own initializations 
     * At the end, the hubot must call the <i>initialized()</i> method to create and start the adapters
     * and routes.
     * @param hClient
     */
    protected void init(HClient hClient) {
        initialized();
    }

    /**
     * Create the hubotAdapter and all adapters declared in the file. Call the <i>startAdapters</i> for 
     * start all adapters
     * Set the status status READY when its over
     */
    protected final void initialized() {
        setStatus(HubotStatus.INITIALIZED);

        //Create HubotAdapter (Mandatory)
        createHubotAdapter();

        //Create other adapters
        createAdapters();
        createRoutes();
        startAdapters();
        createDispatcher();
        setStatus(HubotStatus.READY);
    }

    private void createRoutes() {

        RouteGenerator routes = new RouteGenerator(adapterOutboxActors);
        try {
            camelContext.setRegistry(createRegistry());
            camelContext.addRoutes(routes);
        } catch (Exception e) {
            logger.error(e.toString());
        }

    }

    private void createHubotAdapter() {
        HubotAdapterInbox hubotAdapterInbox = new HubotAdapterInbox();
        HubotAdapterOutbox hubotAdapterOutbox = new HubotAdapterOutbox();
        hubotAdapterInbox.setHClient(hClient);
        hubotAdapterInbox.setCamelContext(camelContext);
        hubotAdapterOutbox.setHClient(hClient);
        hubotAdapterOutbox.setCamelContext(camelContext);

        hubotAdapterInbox.setActor(HUBOT_ADAPTER_INBOX);
        hubotAdapterOutbox.setActor(HUBOT_ADAPTER_OUTBOX);
        hubotAdapterInbox.setProperties(topology);
        hubotAdapterOutbox.setProperties(topology);

        //Launch the HubotAdapter and put him in adapterInstances 
        hubotAdapterInbox.start();
        hubotAdapterOutbox.start();

        adapterInstances.put(HUBOT_ADAPTER_OUTBOX, hubotAdapterOutbox);
        adapterInstances.put(HUBOT_ADAPTER_INBOX, hubotAdapterInbox);

    }

    //Created other Adapters
    @SuppressWarnings("unchecked")
    private void createAdapters() {
        JSONArray adapters = topology.getAdapters();
        // Create instance of all Adapter
        if (adapters != null) {
            try {
                for (int i = 0; i < adapters.length(); i++) {
                    HAdapterConf adapterConf = new HAdapterConf(adapters.getJSONObject(i));
                    if (adapterConf != null) {
                        if (adapterConf.getType() == null) { //ChannelAdapterInbox
                            HChannelAdapterInbox channelAdapterInbox = new HChannelAdapterInbox();
                            channelAdapterInbox.setActor(adapterConf.getActor());
                            channelAdapterInbox.setProperties(adapterConf.getProperties());
                            channelAdapterInbox.setHClient(hClient);
                            channelAdapterInbox.setCamelContext(camelContext);
                            adapterInstances.put(channelAdapterInbox.getActor(), channelAdapterInbox);
                        } else {
                            String nameAdapter = adapterConf.getType();
                            Class<Object> fc;
                            fc = (Class<Object>) Class.forName(nameAdapter);
                            Adapter newAdapter = (Adapter) fc.newInstance();
                            newAdapter.setActor(adapterConf.getActor());
                            newAdapter.setProperties(adapterConf.getProperties());
                            newAdapter.setHClient(hClient);
                            newAdapter.setCamelContext(camelContext);
                            adapterInstances.put(newAdapter.getActor(), newAdapter);
                            if (newAdapter instanceof AdapterOutbox) {
                                adapterOutboxActors.add(newAdapter.getActor());
                            }
                        }
                    }
                }
            } catch (Exception e) {
                logger.error(e.toString());
            }
        }
    }

    private void startAdapters() {
        if (adapterInstances != null) {
            for (String key : adapterInstances.keySet()) {
                adapterInstances.get(key).start();
            }
        }
        try {
            camelContext.start();
        } catch (Exception e) {
            logger.error(e.toString());
        }
    }

    private void createDispatcher() {
        hubotDispatcher = new HubotDispatcher(adapterOutboxActors);
    }

    /**
     * Method used by camel. See Camel documentation for more information
     * @return
     * @throws Exception
     */
    private JndiRegistry createRegistry() throws Exception {
        JndiRegistry jndi = new JndiRegistry();

        jndi.bind("actor", messageDelegate);
        jndi.bind(HUBOT_ADAPTER_OUTBOX, adapterInstances.get(HUBOT_ADAPTER_OUTBOX));

        if (adapterOutboxActors != null) {
            for (String key : adapterOutboxActors) {
                jndi.bind(key, adapterInstances.get(key));
            }
        }
        return jndi;
    }

    protected final void stop() {
        for (String key : adapterInstances.keySet()) {
            adapterInstances.get(key).stop();
        }
        try {
            camelContext.stop();
        } catch (Exception e) {
            logger.error(e.toString());
        }
    }

    protected class MessagesDelegate {
        /* Method use for incoming message/command */
        public final void inProcess(Object obj) {
            if (obj != null) {
                if (obj instanceof HubotMessageStructure) {
                    try {
                        HubotMessageStructure hubotStruct = (HubotMessageStructure) obj;
                        HMessage message = hubotStruct.getMessage();
                        try {
                            HMessageDelegate callback = hubotStruct.getCallback();
                            if (callback != null) {
                                callback.onMessage(message);
                            } else {
                                inProcessMessage(message);
                            }
                        } catch (Exception e) {
                            logger.debug("message: ", e);
                        }
                    } catch (Exception e) {
                        logger.debug("message: ", e);
                    }
                }
            }
        }
    }

    protected abstract void inProcessMessage(HMessage incomingMessage);

    /**
     * Send an HMessage to a specified adapter outbox.
     * @param hmessage
     */
    protected final void send(HMessage hmessage) {
        hubotDispatcher.dispatcher(hmessage, null);
    }

    /**
     * Send an HMessage to another actor.
     * @param hmessage a hmessage to send to a dedicated actor
      * @param callback the callback to call if an answer is sent by the actor called. Note, you must set a timer value
      * on the hMessage. if not the callback will be ingored
     */
    protected final void send(HMessage hmessage, HMessageDelegate callback) {
        hubotDispatcher.dispatcher(hmessage, callback);
    }

    /**
     * Send an HMessage to another actor.
     * @param hmessage a hmessage to send to a dedicated actor
     * @param callback the callback to call if an answer is sent by the actor called. Note, you must set a timer value
     * on the hMessage. if not the callback will be ingored
     * @param timeout the timeout value to use. (ms)
     */
    protected final void send(HMessage hmessage, HMessageDelegate callback, long timeout) {
        hmessage.setTimeout(timeout);
        hubotDispatcher.dispatcher(hmessage, callback);
    }

    private class FilterDelegate implements HMessageDelegate {

        @Override
        public void onMessage(HMessage hMessage) {
            logger.info("Filter : " + hMessage.getPayload());
        }
    }

    /**
     * Check if the hClient is connect and if connected, set the status to STARTED 
     * and launch the init method
     */
    private class StatusDelegate implements HStatusDelegate {
        /* Method use for incoming message/command */
        public final void onStatus(HStatus status) {
            if (status.getStatus() == ConnectionStatus.CONNECTED && outerclass.status == HubotStatus.CREATED) {
                setStatus(HubotStatus.STARTED);
                try {
                    if ((topology.getFilter() != null) && (topology.getFilter().length() > 0)) {
                        hClient.setFilter(topology.getFilter(), new FilterDelegate());
                    }
                } catch (MissingAttrException e) {
                    logger.info("Filter error : ", e);
                }
                init(hClient);
            }
            logger.info("Hubiquitus connection : " + status);
        }
    }

    private void setStatus(HubotStatus status) {
        this.status = status;
        logger.info((topology.getType() + "(" + topology.getActor() + ") : " + status));
    }

    /**
     * Update a specified Adapter's properties
     * @param actor : adapter actor
     * @param properties : params - params for update properties
     */
    protected void updateAdapterProperties(String actor, JSONObject properties) {
        Adapter updatedAdapter = adapterInstances.get(actor);
        updatedAdapter.updateProperties(properties);
    }

    /**
     * Retrived the instance of a specified AdapterInbox
     * You can use this method to modified some properties of this adapter
     * @param actor
     * @return the instance of this adapter
     */
    protected final AdapterInbox getAdapterInbox(String actor) {
        return (AdapterInbox) adapterInstances.get(actor);
    }

    /**
     * Retrived the instance of a specified AdapterOutbox
     * You can use this method to modified some properties of this adapter
     * @param actor
     * @return the instance of this adapter
     */
    protected final AdapterOutbox getAdapterOutbox(String actor) {
        return (AdapterOutbox) adapterInstances.get(actor);
    }

    /**
     * Retrived the properties of this Bot
     * @return a JSONObject for the properties set in the topology of the hubot (could be null)
     */
    protected final JSONObject getProperties() {
        return topology.getProperties();
    }

    /**
     * Helper to create a hMessage. Payload type could be instance of JSONObject(HAlert, HAck, HCommand ...), JSONObject, JSONArray, String, Boolean, Number
     * @param actor : The Hubot for the hMessage. Mandatory.
     * @param type : The type of the hMessage. Not mandatory.
     * @param payload : The payload for the hMessage. Not mandatory.
     * @param options : The options if any to use for the creation of the hMessage. Not mandatory.
      * @return a hMessage which can be used with the send method
     * @throws MissingAttrException 
     */
    public HMessage buildMessage(String actor, String type, Object payload, HMessageOptions options)
            throws MissingAttrException {
        return hClient.buildMessage(actor, type, payload, options);
    }

    /**
     * Helper to create a hMessage with a hConvState payload.
     * @param actor : The channel id for the hMessage. Mandatory
     * @param convid : The convid where the status have to be updated. Mandatory
     * @param status : Status of the conversation. Mandatory.
     * @param options : The options to use if any for the creation of the hMessage. Not mandatory.
     * @return A hMessage with a hConvState payload.
     * @throws MissingAttrException 
     */
    public HMessage buildConvState(String actor, String convid, String status, HMessageOptions options)
            throws MissingAttrException {
        return hClient.buildConvState(actor, convid, status, options);
    }

    /**
     * Helper to create a hMessage wiht a hAck payload.
     * @param actor : The actor for the hMessage.  Mandatory.
     * @param ref : The msgid to acknowledged. Mandatory.
     * @param ack : The following values are authorized : 
     * (1). recv? : means that the message has been received by the participant (on at least one of its devices). 
     * (2). read? : means that the message has been read by the participant.
     * Mandatory.
     * @param options : The options to use if any for the creation of the hMessage. Not mandatory.
     * @return A hMessage with a hAck payload.
     * @throws MissingAttrException 
     */
    public HMessage buildAck(String actor, String ref, HAckValue ack, HMessageOptions options)
            throws MissingAttrException {
        return hClient.buildAck(actor, ref, ack, options);
    }

    /**
     * Helper to create a hMessage with a hAlert payload.
     * @param actor : The channel id for the hMessage. Mandatory.
     * @param alert : The alert message. Mandatory.
     * @param options : The options to use if any for the creation of the hMessage. Not mandatory.
     * @return A hMessage with a hAlert payload.
     * @throws MissingAttrException 
     */
    public HMessage buildAlert(String actor, String alert, HMessageOptions options) throws MissingAttrException {
        return hClient.buildAlert(actor, alert, options);
    }

    /**
     * Helper to create a hMessage with a hMeasure payload.
     * @param actor : The actor for the hMessage. Mandatory
     * @param value : The value of the measure. Mandatory
     * @param unit : The unit of the measure. Mandatory
     * @param options : The options to use if any for the creation of the hMessage. Not Mandatory.
     * @return A hMessage with a hMeasure payload. 
     * @throws MissingAttrException 
     */
    public HMessage buildMeasure(String actor, String value, String unit, HMessageOptions options)
            throws MissingAttrException {
        return hClient.buildMeasure(actor, value, unit, options);
    }

    /**
     * Helper to create a hMessage with a hCommand payload.
     * @param actor : The actor for the hMessage. Mandatory.
     * @param cmd : The name of the command. Mandatory.
     * @param params : Parameters of the command. Not mandatory.
     * @param options : The options to use if any for the creation of the hMessage. Not mandatory.
     * @return A hMessage with a hCommand payload.
     * @throws MissingAttrException 
     */
    public HMessage buildCommand(String actor, String cmd, JSONObject params, HMessageOptions options)
            throws MissingAttrException {
        return hClient.buildCommand(actor, cmd, params, options);
    }

    /**
     * Helper to create a hMessage with a hResult payload.
     * @param actor : The actor for the hMessage. Mandatory.
     * @param ref : The id of the message received, for correlation purpose. Mandatory.
     * @param status : Result status code. Mandatory.
     * @param result : The result String of a command.
     * @param options : The options to use if any for the creation of the hMessage. Not mandatory.
     * @return A hMessage with a hResult payload.
     * @throws MissingAttrException 
     */
    public HMessage buildResult(String actor, String ref, ResultStatus status, String result,
            HMessageOptions options) throws MissingAttrException {
        return hClient.buildResult(actor, ref, status, result, options);
    }

    /**
     * Helper to create a hMessage with a hResult payload.
     * @param actor : The actor for the hMessage. Mandatory.
     * @param ref : The id of the message received, for correlation purpose. Mandatory.
     * @param status : Result status code. Mandatory.
     * @param result : The result boolean of a command.
     * @param options : The options to use if any for the creation of the hMessage. Not mandatory.
     * @return A hMessage with a hResult payload.
     * @throws MissingAttrException 
     */
    public HMessage buildResult(String actor, String ref, ResultStatus status, boolean result,
            HMessageOptions options) throws MissingAttrException {
        return hClient.buildResult(actor, ref, status, result, options);
    }

    /**
     * Helper to create a hMessage with a hResult payload.
     * @param actor : The actor for the hMessage. Mandatory.
     * @param ref : The id of the message received, for correlation purpose. Mandatory.
     * @param status : Result status code. Mandatory.
     * @param result : The result double of a command.
     * @param options : The options to use if any for the creation of the hMessage. Not mandatory.
     * @return A hMessage with a hResult payload.
     * @throws MissingAttrException 
     */
    public HMessage buildResult(String actor, String ref, ResultStatus status, double result,
            HMessageOptions options) throws MissingAttrException {
        return hClient.buildResult(actor, ref, status, result, options);
    }

    /**
     * Helper to create a hMessage with a hResult payload.
     * @param actor : The actor for the hMessage. Mandatory.
     * @param ref : The id of the message received, for correlation purpose. Mandatory.
     * @param status : Result status code. Mandatory.
     * @param result : The result JSONArray of a command.
     * @param options : The options to use if any for the creation of the hMessage. Not mandatory.
     * @return A hMessage with a hResult payload.
     * @throws MissingAttrException 
     */
    public HMessage buildResult(String actor, String ref, ResultStatus status, JSONArray result,
            HMessageOptions options) throws MissingAttrException {
        return hClient.buildResult(actor, ref, status, result, options);
    }

    /**
     * Helper to create a hMessage with a hResult payload.
     * @param actor : The actor for the hMessage. Mandatory.
     * @param ref : The id of the message received, for correlation purpose. Mandatory.
     * @param status : Result status code. Mandatory.
     * @param result : The result JSONObject of a command.
     * @param options : The options to use if any for the creation of the hMessage. Not mandatory.
     * @return A hMessage with a hResult payload.
     * @throws MissingAttrException 
     */
    public HMessage buildResult(String actor, String ref, ResultStatus status, JSONObject result,
            HMessageOptions options) throws MissingAttrException {
        return hClient.buildResult(actor, ref, status, result, options);
    }
}