/*
* 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.services;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Map;
import org.apache.felix.main.AutoProcessor;
import org.json.JSONArray;
import org.json.JSONObject;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.launch.FrameworkFactory;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder;
import android.os.IBinder;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Base64;
import android.util.Log;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.R;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.commontools.Unzip;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.events.Event;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.events.localmanagement.EventTransformationResponse;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.services.transformationmanager.TransformationManager;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.services.transformationmanager.database.Transformation;
public class FelixService extends Service implements IFelixServiceBinder {
// for debugging
private static final String TAG = "FelixService";
private static final boolean D = false;
// intent extra for transmitting server response
public static final String INTENT_EXTRA_TRANSFORMATION_BYTE_CODE = "transformationByteCode";
public static final String FELIX_SUCCESSFUL_WEB_REQUEST = "felixSuccessfullWebRequest";
// felix framework
private Framework felixFramework;
private final BroadcastReceiver downloadReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
EventTransformationResponse response = intent.getParcelableExtra(Event.PARCELABLE_EXTRA_EVENT);
// if no transformation has been found, we cannot load it.
if(!response.isTransformationFound()) return;
// get server response
String server_response = intent.getStringExtra(INTENT_EXTRA_TRANSFORMATION_BYTE_CODE);
try {
JSONObject jsObject = new JSONObject(server_response);
// get transmitted bundle as String and decode it to Bytes
String bundleURI = jsObject.getString(WebRequestService.JSON_TRANSFORMATION_URI);
String bundle = jsObject.getString(WebRequestService.JSON_TRANSFORMATION_BYTE_CODE);
byte[] bytes = Base64.decode(bundle, Base64.DEFAULT);
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
// Load new bundle in Felix framework
Bundle transformatorBundle = felixFramework.getBundleContext()
.installBundle(bundleURI, inputStream);
// Start transformation
startTransformation(transformatorBundle.getBundleId());
if(D)Log.i(TAG, "New transformation deployed: " +
transformatorBundle.getSymbolicName() + " (with ID: "+transformatorBundle.getBundleId()+")");
// Get array of required event types
ArrayList<String> requiredEventTypes = new ArrayList<String>();
JSONArray temp = jsObject.getJSONArray(WebRequestService.JSON_REQUIRED_EVENT_TYPES);
for(int i = 0; i < temp.length(); i++) {
requiredEventTypes.add(temp.getString(i));
}
// Create transformation object
Transformation transformation = new Transformation(
transformatorBundle.getBundleId(),
bundleURI,
requiredEventTypes,
jsObject.getString(WebRequestService.JSON_REQUESTED_EVENT_TYPE),
jsObject.getInt(WebRequestService.JSON_TRANSFORMATION_COSTS));
// Send response to TransformationManager
Intent i = new Intent();
i.putExtra(Event.PARCELABLE_EXTRA_EVENT, response);
i.putExtra(Event.PARCELABLE_EXTRA_EVENT_TYPE, response.getEventType());
i.putExtra(TransformationManager.INTENT_EXTRA_TRANSFORMATION, transformation);
i.setAction(TransformationManager.TM_SUCCUESSFUL_WEB_REQUEST);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(i);
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
@Override
public void onCreate() {
super.onCreate();
if(D) Log.d(TAG, "Setting up a thread for felix.");
Thread felixThread = new Thread() {
@Override
public void run() {
File dexOutputDir = getApplicationContext().getDir("transformationmanager", 0);
// if default bundles were not installed already, install them
File f = new File(dexOutputDir.getAbsolutePath()+"/bundle");
if(!f.isDirectory()) {
if(D) Log.i(TAG, "Installing default bundles...");
unzipBundles(FelixService.this.getResources().openRawResource(R.raw.bundles),
dexOutputDir.getAbsolutePath()+"/");
}
FelixConfig felixConfig = new FelixConfig(dexOutputDir.getAbsolutePath());
Map<String, String> configProperties = felixConfig.getProperties2();
try {
FrameworkFactory frameworkFactory = new org.apache.felix.framework.FrameworkFactory();
felixFramework = frameworkFactory.newFramework(configProperties);
felixFramework.init();
AutoProcessor.process(configProperties,felixFramework.getBundleContext());
felixFramework.start();
// Registering the android context as an osgi service
Hashtable<String, String> properties = new Hashtable<String, String>();
properties.put("platform", "android");
felixFramework.getBundleContext().registerService(
Context.class.getName(), getApplicationContext(),
properties);
} catch (Exception ex) {
Log.e(TAG, "Felix could not be started", ex);
ex.printStackTrace();
}
}
};
felixThread.setDaemon(true);
felixThread.start();
LocalBroadcastManager.getInstance(this).registerReceiver(downloadReceiver,
new IntentFilter(FELIX_SUCCESSFUL_WEB_REQUEST));
}
private void unzipBundles(InputStream rawResource, String internalDataDir) {
Unzip decompress = new Unzip(rawResource, internalDataDir);
decompress.unzip();
}
@Override
public void onDestroy() {
super.onDestroy();
if(D)Log.i(TAG, "stopping the felix service");
try {
if (felixFramework != null)
felixFramework.stop();
felixFramework = null;
} catch (BundleException ex) {
Log.w(TAG, "problem occuring when stopping felix", ex);
}
}
@Override
public IBinder onBind(Intent intent) {
return new FelixServiceBinder();
}
public class FelixServiceBinder extends Binder {
public FelixService getService() {
return FelixService.this;
}
}
@Override
public void startTransformation(long bundleId) {
Bundle transformatorBundle = felixFramework.getBundleContext()
.getBundle(bundleId);
if(D)Log.i(TAG, "Starting bundle "+bundleId+": "+transformatorBundle.getSymbolicName());
try {
transformatorBundle.start();
} catch (BundleException e) {
Log.w("Bundle cannot be started", e);
}
}
@Override
public void stopTransformation(long bundleId) {
Bundle transformatorBundle = felixFramework.getBundleContext()
.getBundle(bundleId);
if(D)Log.i(TAG, "Stopping bundle "+bundleId+": "+transformatorBundle.getSymbolicName());
try {
transformatorBundle.stop();
} catch (BundleException e) {
Log.w("Bundle cannot be stopped", e);
}
}
@Override
public void removeTransformation(long bundleId) {
Bundle transformatorBundle = felixFramework.getBundleContext()
.getBundle(bundleId);
if(D)Log.i(TAG, "Uninstalling bundle "+bundleId+": "+transformatorBundle.getSymbolicName());
try {
transformatorBundle.uninstall();
} catch (BundleException e) {
Log.w("bundle cannot be removed", e);
}
}
@Override
public Bundle[] getTransformations() {
return felixFramework.getBundleContext().getBundles();
}
}