com.ricston.connectors.dataanalysis.DataAnalysisConnector.java Source code

Java tutorial

Introduction

Here is the source code for com.ricston.connectors.dataanalysis.DataAnalysisConnector.java

Source

/**
 * (c) 2003-2014 Ricston, Ltd. The software in this package is published under the terms of the CPAL v1.0 license,
 * a copy of which has been included with this distribution in the LICENSE.md file.
 */

package com.ricston.connectors.dataanalysis;

import java.io.File;
import java.io.IOError;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;

import javax.inject.Inject;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import javax.management.StandardMBean;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mapdb.DB;
import org.mapdb.DBMaker;
import org.mule.api.MuleContext;
import org.mule.api.annotations.Connector;
import org.mule.api.annotations.Processor;
import org.mule.api.annotations.expressions.Expr;
import org.mule.api.annotations.lifecycle.Start;
import org.mule.api.annotations.lifecycle.Stop;
import org.mule.util.FileUtils;
import org.mule.util.UUID;

/**
 * Data Analysis Connector. 
 * 
 * Collect KPIs from your flows and persist them temporarily until
 * the statistics collector reads all the data and publishes for analysis.
 *
 * @author Ricston Ltd.
 */
@Connector(name = "dataanalysis", schemaVersion = "1.0", friendlyName = "DataAnalysis")
public class DataAnalysisConnector implements DataAnalysisMBean {
    /**
     * MapDb database
     */
    private DB db;

    /**
     * Logger
     */
    protected Log logger = LogFactory.getLog(getClass());

    /**
     * MBean name to use to expose the data
     */
    private String MBEAN_NAME = "Mule.%s:type=com.ricston.dataanalysis,name=kpi";

    /**
     * Mule application name 
     */
    private String application;

    /**
     * The Mule Context
     */
    @Inject
    private MuleContext muleContext;

    /**
     * Start the module by registering to the platform Mbean server, and starting the Map DB
     * 
     * @throws MalformedObjectNameException
     * @throws NotCompliantMBeanException
     * @throws InstanceAlreadyExistsException
     * @throws MBeanRegistrationException
     * @throws IOException 
     */
    @Start
    public void startModule() throws MalformedObjectNameException, NotCompliantMBeanException,
            InstanceAlreadyExistsException, MBeanRegistrationException, IOException {
        application = muleContext.getConfiguration().getId();

        logger.info("**********************************");
        logger.info("*Starting Data Analysis Connector*");
        logger.info("**********************************");

        startMapDb();

        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        ObjectName id = new ObjectName(String.format(MBEAN_NAME, application));
        StandardMBean mbean = new StandardMBean(this, DataAnalysisMBean.class);
        server.registerMBean(mbean, id);

        logger.info("Registered mbean using name: " + String.format(MBEAN_NAME, application));
    }

    /**
     * Stop the module by closing the Map DB and unregister from the Mbean server
     * 
     * @throws MalformedObjectNameException
     * @throws MBeanRegistrationException
     * @throws InstanceNotFoundException
     */
    @Stop
    public void stopModule()
            throws MalformedObjectNameException, MBeanRegistrationException, InstanceNotFoundException {
        logger.info("**********************************");
        logger.info("*Stopping Data Analysis Connector*");
        logger.info("**********************************");

        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        ObjectName id = new ObjectName(String.format(MBEAN_NAME, application));
        server.unregisterMBean(id);

        logger.info("Unregistered mbean with name: " + String.format(MBEAN_NAME, application));

        stopMapDb();
    }

    /**
     * Create a database with all the necessary settings
     * 
     * @param dbFile File backing up the MapDb
     * @return The MapDb
     */
    protected DB makeDb(File dbFile) {
        return DBMaker.newFileDB(dbFile).transactionDisable().asyncWriteEnable().commitFileSyncDisable().make();
    }

    /**
     * Start the MapDb
     * @throws IOException 
     */
    private void startMapDb() throws IOException {
        String workingDir = muleContext.getConfiguration().getWorkingDirectory();
        String dbFolderPath = workingDir + "/dataanalysis/" + application;
        String dbFilePath = dbFolderPath + "/mapdb.dat";
        FileUtils.forceMkdir(new File(dbFolderPath));
        File dbFile = new File(dbFilePath);

        logger.info("Using " + dbFilePath + " as a store for analysis data persistency");

        //try to create the database
        try {
            db = makeDb(dbFile);
        } catch (IOError exc) {
            //if store was corrupted, back it up and create a new one
            logger.error(
                    "Store is corrupted. A backup of the current store will be taken, and a new store will be created.");
            FileUtils.renameFile(dbFile, new File(dbFolderPath + "/" + UUID.getUUID() + ".backup"));
            db = makeDb(dbFile);
        }
    }

    /**
     * Stop the MapDb
     */
    private void stopMapDb() {
        if (db != null) {
            db.close();
            db = null;
        }
    }

    /**
     * Collect for Analysis processor. Accepts a map of key performance indicators (KPIs) and stores
     * the data in a MapDb backed persisted queue. Data is enhanced with current time, and message id.
     *
     * {@sample.xml ../../../doc/dataanalysis-connector.xml.sample dataanalysis:collect-for-analysis}
     *
     * @param kpiName name for the key performance indicator
     * @param data Key value pairs of data to collect
     * @param messageId the message id
     */
    @Processor
    public void collectForAnalysis(String kpiName, Map<String, Object> data,
            @Expr("#[message.id]") String messageId) {
        data.put("kpiName", kpiName);
        data.put("type", "KPI");
        data.put("messageId", messageId);
        data.put("application", application);
        data.put("@timestamp", new Date());

        BlockingQueue<Map<String, Object>> queue = db.getStack(kpiName);
        queue.add(data);

    }

    /**
     * Get the Mule Context
     * 
     * @return the Mule Context
     */
    public MuleContext getMuleContext() {
        return muleContext;
    }

    /**
     * Set the Mule Context
     * 
     * @param muleContext
     */
    public void setMuleContext(MuleContext muleContext) {
        this.muleContext = muleContext;
    }

    /**
     * Take the data from the MapDb backed persisted queue and return it as a List of Maps.
     * The idea behind this method is that the statistics collector will poll this method
     * for data every couple of seconds.
     * 
     */
    @Override
    public List<Map<String, Object>> getData() {
        logger.debug("Retrieving analysis data");

        Map<String, Object> all = db.getAll();

        List<Map<String, Object>> items = new ArrayList<Map<String, Object>>();

        for (Iterator<Map.Entry<String, Object>> i = all.entrySet().iterator(); i.hasNext();) {
            @SuppressWarnings("unchecked")
            BlockingQueue<Map<String, Object>> q = (BlockingQueue<Map<String, Object>>) i.next().getValue();
            q.drainTo(items);
        }

        logger.debug("Retrieving complete with total elements: " + items.size());

        return items;
    }

}