de.tudarmstadt.dvs.myhealthassistant.myhealthhub.services.transformationmanager.TransformationManager.java Source code

Java tutorial

Introduction

Here is the source code for de.tudarmstadt.dvs.myhealthassistant.myhealthhub.services.transformationmanager.TransformationManager.java

Source

/* 
 * Copyright (C) 2014 TU Darmstadt, Hessen, Germany.
 * Department of Computer Science Databases and Distributed Systems
 *
 * 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 3 of the License, 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
* 
*/
package de.tudarmstadt.dvs.myhealthassistant.myhealthhub.services.transformationmanager;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import org.osgi.framework.Bundle;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.events.AbstractChannel;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.events.Event;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.events.localmanagement.EventTransformationRequest;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.events.localmanagement.EventTransformationResponse;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.events.management.Announcement;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.events.management.StopProducer;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.services.transformationmanager.database.LocalTransformationDBMS;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.services.transformationmanager.database.Transformation;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.services.transformationmanager.services.FelixService;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.services.transformationmanager.services.FelixService.FelixServiceBinder;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.services.transformationmanager.services.IFelixServiceBinder;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.services.transformationmanager.services.WebRequestService;

/**
 * @author Christian Seeger
 *
 */
public class TransformationManager extends Service {

    /** For Debugging */
    private static final String TAG = "TransformationManager";
    private static boolean D = false;

    // receives management events such as transformation requests
    private ManagementReceiver mManagementReceiver;
    private TransformationDownloadReceiver mDownloadReceiver;

    // database including already downloaded availableTransformations
    private LocalTransformationDBMS transformationDB;

    // web request channel
    private static String WEB_REQUEST_CHANNEL = "webRequestChannel";
    public static String TM_SUCCUESSFUL_WEB_REQUEST = "tmSuccessfulWebRequest";
    public static final String INTENT_EXTRA_TRANSFORMATION = "transformation";

    // service binder to Felix OSGi service
    private IFelixServiceBinder felixServiceBinder;

    // list of available availableTransformations
    private ArrayList<Transformation> availableTransformations;

    // for transformation management (stopping transformations)
    private HashMap<String, List<Transformation>> requiredEventTypes;
    private HashMap<String, List<Transformation>> providedEventTypes;
    private LinkedList<Transformation> runningTransformations;

    private final IBinder mTransformationManagerBinder = new TransformationManagerBinder();

    public class TransformationManagerBinder extends Binder {
        public Bundle[] getTransformations() {
            if (felixServiceBinder != null) {
                return felixServiceBinder.getTransformations();
            } else
                return null;
        }

        public void deleteTransformation(long name) {
            if (felixServiceBinder != null)
                felixServiceBinder.removeTransformation(name);
            if (transformationDB != null)
                transformationDB.deleteTransformation(name);
            availableTransformations = transformationDB.getAvailableTransformations();
        }

        public void stopTransformation(long name) {
            if (felixServiceBinder != null)
                felixServiceBinder.stopTransformation(name);
        }

        public void startTransformation(long name) {
            if (felixServiceBinder != null)
                felixServiceBinder.startTransformation(name);
        }
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return mTransformationManagerBinder;
    }

    public void onUnbind() {
    }

    @Override
    public void onCreate() {
        if (D)
            Log.i(TAG, "TransformationManager created.");

        // start Felix OSGi service 
        Intent intent = new Intent(this, FelixService.class);
        bindService(intent, felixServiceConnection, Context.BIND_AUTO_CREATE);

        // register local management receiver
        mManagementReceiver = new ManagementReceiver();
        LocalBroadcastManager.getInstance(this).registerReceiver(mManagementReceiver,
                new IntentFilter(AbstractChannel.LOCAL_MANAGEMENT));
        LocalBroadcastManager.getInstance(this).registerReceiver(mManagementReceiver,
                new IntentFilter(WEB_REQUEST_CHANNEL));

        // register transformation download receiver
        mDownloadReceiver = new TransformationDownloadReceiver();
        LocalBroadcastManager.getInstance(this).registerReceiver(mDownloadReceiver,
                new IntentFilter(TM_SUCCUESSFUL_WEB_REQUEST));

        // initialize database
        this.transformationDB = new LocalTransformationDBMS(getApplicationContext());

        //TODO better solution
        transformationDB.open();
        availableTransformations = transformationDB.getAvailableTransformations();
        if (D)
            printLocalTransformations();

        // initialize management lists
        providedEventTypes = new HashMap<String, List<Transformation>>();
        requiredEventTypes = new HashMap<String, List<Transformation>>();
        runningTransformations = new LinkedList<Transformation>();
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, "TransformationManager destroyed.");

        // stop all transformations
        for (Transformation transformation : runningTransformations) {
            if (D)
                Log.d(TAG, "Stop transformation: " + transformation.getTransformationName());
            if (felixServiceBinder != null)
                felixServiceBinder.stopTransformation(transformation.getBundleId());
        }

        // stop felix service
        unbindService(felixServiceConnection);

        // unregister local management receiver
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mManagementReceiver);
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mDownloadReceiver);

        // close database
        transformationDB.close();
    }

    /** Management Receiver */
    private class ManagementReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            // extract event
            Event evt = intent.getParcelableExtra(Event.PARCELABLE_EXTRA_EVENT);

            // handle event transformation requests
            if (evt instanceof EventTransformationRequest) {
                incomingTransformationRequest((EventTransformationRequest) evt);
            } else if (evt instanceof Announcement) {
                if (((Announcement) evt)
                        .getAnnouncement() == Announcement.UNADVERTISMENT_EVENT_TYPE_NOT_LONGER_AVAILABLE)
                    incomingUnadvertisement(((Announcement) evt).getTransmittedEventType());
            } else if (evt instanceof StopProducer) {
                incomingStopProducer((StopProducer) evt);
            }
        }
    };

    private void incomingUnadvertisement(String eventType) {
        if (D)
            Log.d(TAG, "Handle unadvertisement for " + eventType);

        // Check whether transformations require this event type
        List<Transformation> transformations = requiredEventTypes.get(eventType);
        if (transformations != null)
            deleteTransformationsFromManagementLists(transformations);
    }

    public void incomingStopProducer(StopProducer stop) {
        if (D)
            Log.d(TAG, "Handle stop producer for " + stop.getShortEventType());

        // Check whether a transformation produces this event type
        List<Transformation> transformations = providedEventTypes.get(stop.getStopEventType());
        if (transformations != null)
            deleteTransformationsFromManagementLists(transformations);
    }

    private void deleteTransformationsFromManagementLists(List<Transformation> transformations) {
        // stop corresponding transformations
        for (Transformation transformation : transformations) {
            // delete from running transformations
            runningTransformations.remove(transformation);

            // stop transformation
            if (felixServiceBinder != null)
                felixServiceBinder.stopTransformation(transformation.getBundleId());

            // delete required event types
            List<String> reqTypes = transformation.getRequiredEventTypes();
            for (String type : reqTypes) {
                requiredEventTypes.get(type).remove(transformation);
            }

            // delete produced event type
            providedEventTypes.get(transformation.getProducedEventType()).remove(transformation);
        }
    }

    private void addToRunningTransformations(Transformation transformation) {
        // Add to running transformations
        runningTransformations.add(transformation);

        // Add to list of provided event types
        List<Transformation> producers = providedEventTypes.get(transformation.getProducedEventType());
        if (producers != null) {
            producers.add(transformation);
        } else {
            producers = new ArrayList<Transformation>();
            producers.add(transformation);
        }
        providedEventTypes.put(transformation.getProducedEventType(), producers);

        // Add to list of required event types
        List<String> requiredTypes = transformation.getRequiredEventTypes();
        for (String eventType : requiredTypes) {
            List<Transformation> transformations = requiredEventTypes.get(eventType);
            if (transformations != null) {
                transformations.add(transformation);
            } else {
                transformations = new ArrayList<Transformation>();
                transformations.add(transformation);
            }
            requiredEventTypes.put(eventType, transformations);
        }
    }

    /** Transformation Download Receiver */
    private class TransformationDownloadReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {

            EventTransformationResponse response = intent.getParcelableExtra(Event.PARCELABLE_EXTRA_EVENT);

            if (response.isTransformationFound()) {
                Transformation transformation = intent.getParcelableExtra(INTENT_EXTRA_TRANSFORMATION);
                if (D)
                    Log.d(TAG, "Transformation was found: " + transformation.getTransformationName());

                // Add new and already started transformation to DB 
                if (transformationDB != null)
                    transformationDB.addTransformation(transformation.getBundleId(),
                            transformation.getTransformationName(), transformation.getProducedEventType(),
                            transformation.getRequiredEventTypes(), transformation.getTransformationCost());

                // Add transformation to list of availableTransformations
                availableTransformations.add(transformation);

                // Add to running transformations
                addToRunningTransformations(transformation);

            } else {
                if (D)
                    Log.d(TAG, "No transformation found.");
                //TODO
            }
        }
    };

    /**
     * @param transformationRequest
     */
    private void incomingTransformationRequest(EventTransformationRequest transformationRequest) {
        if (D)
            printTransformationRequest(transformationRequest);

        // skip request if no event types are advertised
        if (transformationRequest.getAdvertisedEvents().length == 0)
            return;

        // is transformation stored locally?
        Transformation trans = getLocalTransformation(transformationRequest);

        if (trans != null) {
            if (felixServiceBinder != null) {
                felixServiceBinder.startTransformation(trans.getBundleId());
                addToRunningTransformations(trans);
            } else {
                if (D)
                    Log.e(TAG, "not connected to felix service.");
            }

            // if not, query remote repository
        } else {
            Log.i(TAG, "Query remote repository for transformation to: "
                    + transformationRequest.getEventSubscription());
            Intent intentRequest = new Intent(getApplicationContext(), WebRequestService.class);
            intentRequest.putExtra(Event.PARCELABLE_EXTRA_EVENT, transformationRequest);
            getApplicationContext().startService(intentRequest);
        }
    }

    /**
      * Queries for local transformation that is applicable and has the lowest costs.
     * @param transformationRequest containing the available event types and the requested event types.
     * @return transformation. <code>null</code> if no transformation was found.
     */
    private Transformation getLocalTransformation(EventTransformationRequest transformationRequest) {
        //TODO make it efficient
        ArrayList<String> list = new ArrayList<String>();
        String[] advertisedEvents = transformationRequest.getAdvertisedEvents();
        for (int i = 0; i < advertisedEvents.length; i++) {
            list.add(advertisedEvents[i]);
        }

        // Find a transformation
        Transformation bestTransformation = null;
        int costs = Integer.MAX_VALUE;
        int tempCosts;
        for (Transformation trans : availableTransformations) {
            tempCosts = trans.isTransformationApplicable(list, transformationRequest.getEventSubscription());
            // if the transformation is applicable and the costs are lower than before, store
            if (tempCosts != -1 && tempCosts < costs) {
                costs = tempCosts;
                bestTransformation = trans;
            }
        }

        return bestTransformation;
    }

    private ServiceConnection felixServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            felixServiceBinder = null;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            FelixServiceBinder binder = ((FelixServiceBinder) service);
            felixServiceBinder = (IFelixServiceBinder) binder.getService();
        }
    };

    /* =============================================================================
        helper methods 
        ============================================================================= */
    /**
      * Prints a transformation request.
      * @param req event transformation request
      */
    private void printTransformationRequest(EventTransformationRequest req) {
        String out = "Incoming event transformation request for event type\n";
        out += req.getEventSubscription();
        out += "\nhaving the following event types available:";
        for (String type : req.getAdvertisedEvents()) {
            out += "\n" + type;
        }
        Log.d(TAG, out);
    }

    /**
     * Returns only the last part after the "." of an event type. 
     * @return Short event type
     */
    public String getShortEventType(String eventType) {
        if (eventType.contains("myhealthassistant.event.")) {
            return eventType.substring(eventType.lastIndexOf("myhealthassistant.event.") + 24, eventType.length());
        } else {
            return eventType;
        }

    }

    private void printLocalTransformations() {
        String out = "List of available availableTransformations\n";
        out += "ID | Name\n";
        for (Transformation trans : availableTransformations) {
            out += trans.getBundleId() + " | " + trans.getTransformationName() + "\n";
        }
        Log.d(TAG, out);
    }
}