edu.mit.media.funf.pipeline.BasicPipeline.java Source code

Java tutorial

Introduction

Here is the source code for edu.mit.media.funf.pipeline.BasicPipeline.java

Source

/**
 * 
 * Funf: Open Sensing Framework Copyright (C) 2013 Alan Gardner
 * 
 * This file is part of Funf.
 * 
 * Funf is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
 * General Public License as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * Funf 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 Lesser
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with Funf. If not,
 * see <http://www.gnu.org/licenses/>.
 * 
 */
package edu.mit.media.funf.pipeline;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

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

import edu.mit.media.funf.FunfManager;
import edu.mit.media.funf.Schedule;
import edu.mit.media.funf.config.ConfigUpdater;
import edu.mit.media.funf.config.Configurable;
import edu.mit.media.funf.config.RuntimeTypeAdapterFactory;
import edu.mit.media.funf.json.IJsonObject;
import edu.mit.media.funf.probe.Probe.DataListener;
import edu.mit.media.funf.probe.builtin.ProbeKeys;
import edu.mit.media.funf.storage.DatabaseHelper;
import edu.mit.media.funf.storage.DefaultArchive;
import edu.mit.media.funf.storage.FileArchive;
import edu.mit.media.funf.storage.RemoteFileArchive;
import edu.mit.media.funf.storage.UploadService;
import edu.mit.media.funf.time.TimeUtil;
import edu.mit.media.funf.util.LogUtil;

public class BasicPipeline implements Pipeline, DataListener {

    public static final String ACTION_ARCHIVE = "archive", ACTION_UPLOAD = "upload", ACTION_UPDATE = "update";

    protected final int ARCHIVE = 0, UPLOAD = 1, UPDATE = 2, DATA = 3;

    @Configurable
    protected String name = "default";

    @Configurable
    protected int version = 1;

    @Configurable
    protected FileArchive archive = null;

    protected FileArchive userArchive = null;

    @Configurable
    protected RemoteFileArchive upload = null;

    @Configurable
    protected ConfigUpdater update = null;

    @Configurable
    protected List<JsonElement> data = new ArrayList<JsonElement>();

    @Configurable
    protected Map<String, Schedule> schedules = new HashMap<String, Schedule>();

    private UploadService uploader;

    private boolean enabled;
    private FunfManager manager;
    private SQLiteOpenHelper databaseHelper = null;
    private Looper looper;
    private Handler handler;
    private Handler.Callback callback = new Handler.Callback() {

        @Override
        public boolean handleMessage(Message msg) {

            onBeforeRun(msg.what, (JsonObject) msg.obj);
            switch (msg.what) {
            case ARCHIVE:
                if (archive != null) {
                    runArchive();
                }
                break;
            case UPLOAD:
                if (archive != null && upload != null && uploader != null) {
                    uploader.run(archive, upload);
                }
                break;
            case UPDATE:
                if (update != null) {
                    update.run(name, manager);
                }
                break;
            case DATA:
                String name = ((JsonObject) msg.obj).get("name").getAsString();
                IJsonObject data = (IJsonObject) ((JsonObject) msg.obj).get("value");
                writeData(name, data);
                break;
            default:
                break;
            }
            onAfterRun(msg.what, (JsonObject) msg.obj);
            return false;
        }
    };

    protected void reloadDbHelper(Context ctx) {

        this.databaseHelper = new DatabaseHelper(ctx);
    }

    protected void runArchive() {

        SQLiteDatabase db = databaseHelper.getWritableDatabase();
        // TODO: add check to make sure this is not empty
        File dbFile = new File(db.getPath());
        db.close();
        if (archive.add(dbFile)) {
            dbFile.delete();
        }
        reloadDbHelper(manager);
        db = databaseHelper.getWritableDatabase(); // Build new database
        db.close();
    }

    protected void writeData(String name, IJsonObject data) {

        SQLiteDatabase db = databaseHelper.getWritableDatabase();
        final double timestamp = data.get(ProbeKeys.BaseProbeKeys.TIMESTAMP).getAsDouble();
        final String value = data.toString();
        if (timestamp == 0L || name == null || value == null) {
            Log.e(LogUtil.TAG, "Unable to save data.  Not all required values specified. " + timestamp + " " + name
                    + " - " + value);
            throw new SQLException("Not all required fields specified.");
        }

        String[] subStrings = name.split("\\.");
        String probeName = subStrings[subStrings.length - 1];

        ContentValues cv = new ContentValues();
        cv.put(DatabaseHelper.TABLE_STANDARD_PROBE, name);
        cv.put(DatabaseHelper.TABLE_STANDARD_VALUE, data.toString());
        cv.put(DatabaseHelper.TABLE_STANDARD_TIMESTAMP, TimeUtil.alignedTimestamp);
        for (String entry : data.getAllKeys())
            if (DatabaseHelper.getAllowedList().contains(entry.toLowerCase(Locale.ENGLISH))) {
                String toSave = data.toString();
                try {
                    toSave = data.get(entry).getAsString();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                cv.put(entry, toSave);
            }

        db.insertOrThrow(probeName, "", cv);

        Log.e("Database", name + " " + TimeUtil.alignedTimestamp);

        Intent intent = new Intent("pipeline");
        intent.putExtra("Name", name);
        intent.putExtra("IJsonObject", data.toString());
        // intent.setAction("edu.mit.media.funf.pipeline.broadcastreceiver");
        LocalBroadcastManager.getInstance(getFunfManager()).sendBroadcast(intent);
        db.close();
    }

    @Override
    public void onCreate(FunfManager manager) {

        if (archive == null) {
            archive = new DefaultArchive(manager, name);
        }
        if (uploader == null) {
            uploader = new UploadService(manager);
            uploader.start();
        }
        this.manager = manager;
        reloadDbHelper(manager);
        HandlerThread thread = new HandlerThread(getClass().getName());
        thread.start();
        this.looper = thread.getLooper();
        this.handler = new Handler(looper, callback);
        enabled = true;
        for (JsonElement dataRequest : data) {
            manager.requestData(this, dataRequest);
        }
        for (Map.Entry<String, Schedule> schedule : schedules.entrySet()) {
            manager.registerPipelineAction(this, schedule.getKey(), schedule.getValue());
        }
    }

    @Override
    public void onDestroy() {

        for (JsonElement dataRequest : data) {
            manager.unrequestData(this, dataRequest);
        }
        for (Map.Entry<String, Schedule> schedule : schedules.entrySet()) {
            manager.unregisterPipelineAction(this, schedule.getKey());
        }
        if (uploader != null) {
            uploader.stop();
        }
        looper.quit();
        enabled = false;
    }

    @Override
    public void onRun(String action, JsonElement config) {

        // Run on handler thread
        if (ACTION_ARCHIVE.equals(action)) {
            handler.obtainMessage(ARCHIVE, config).sendToTarget();
        } else if (ACTION_UPLOAD.equals(action)) {
            handler.obtainMessage(UPLOAD, config).sendToTarget();
        } else if (ACTION_UPDATE.equals(action)) {
            handler.obtainMessage(UPDATE, config).sendToTarget();
        }
    }

    /**
     * Used as a hook to customize behavior before an action takes place.
     * 
     * @param action
     *            the type of action taking place
     * @param config
     *            the configuration for the action
     */
    protected void onBeforeRun(int action, JsonElement config) {

    }

    /**
     * Used as a hook to customize behavior after an action takes place.
     * 
     * @param action
     *            the type of action taking place
     * @param config
     *            the configuration for the action
     */
    protected void onAfterRun(int action, JsonElement config) {

    }

    protected Handler getHandler() {

        return handler;
    }

    protected FunfManager getFunfManager() {

        return manager;
    }

    public SQLiteDatabase getDb() {

        return databaseHelper.getReadableDatabase();
    }

    public List<JsonElement> getDataRequests() {

        return data == null ? null : Collections.unmodifiableList(data);
    }

    @Override
    public boolean isEnabled() {

        return enabled;
    }

    public String getName() {

        return name;
    }

    public void setName(String name) {

        this.name = name;
    }

    public int getVersion() {

        return version;
    }

    public void setVersion(int version) {

        this.version = version;
    }

    public FileArchive getArchive() {

        return archive;
    }

    public void setArchive(FileArchive archive) {

        this.archive = archive;
    }

    public RemoteFileArchive getUpload() {

        return upload;
    }

    public void setUpload(RemoteFileArchive upload) {

        this.upload = upload;
    }

    public ConfigUpdater getUpdate() {

        return update;
    }

    public void setUpdate(ConfigUpdater update) {

        this.update = update;
    }

    public void setDataRequests(List<JsonElement> data) {

        this.data = new ArrayList<JsonElement>(data); // Defensive copy
    }

    public Map<String, Schedule> getSchedules() {

        return schedules;
    }

    public void setSchedules(Map<String, Schedule> schedules) {

        this.schedules = schedules;
    }

    public UploadService getUploader() {

        return uploader;
    }

    public void setUploader(UploadService uploader) {

        this.uploader = uploader;
    }

    public SQLiteOpenHelper getDatabaseHelper() {

        return databaseHelper;
    }

    public void setDatabaseHelper(SQLiteOpenHelper databaseHelper) {

        this.databaseHelper = databaseHelper;
    }

    @Override
    public void onDataReceived(IJsonObject probeConfig, IJsonObject data) {

        JsonObject record = new JsonObject();
        record.add("name", probeConfig.get(RuntimeTypeAdapterFactory.TYPE));
        record.add("value", data);
        handler.obtainMessage(DATA, record).sendToTarget();
    }

    @Override
    public void onDataCompleted(IJsonObject probeConfig, JsonElement checkpoint) {

        // TODO Figure out what to do with continuations of probes, if anything

    }
}