interactivespaces.controller.android.InteractiveSpacesFrameworkAndroidBootstrap.java Source code

Java tutorial

Introduction

Here is the source code for interactivespaces.controller.android.InteractiveSpacesFrameworkAndroidBootstrap.java

Source

/*
 * Copyright (C) 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package interactivespaces.controller.android;

import interactivespaces.controller.internal.osgi.OsgiControllerActivator;
import interactivespaces.service.androidos.AndroidOsService;
import interactivespaces.service.androidos.impl.SimpleAndroidOsService;
import interactivespaces.system.bootstrap.osgi.GeneralInteractiveSpacesSupportActivator;
import interactivespaces.system.core.configuration.ConfigurationProvider;
import interactivespaces.system.core.container.ContainerCustomizerProvider;
import interactivespaces.system.core.container.SimpleContainerCustomizerProvider;
import interactivespaces.system.core.logging.LoggingProvider;

import android.content.Context;
import android.content.res.AssetManager;
import android.preference.PreferenceManager;
import android.util.Log;
import org.apache.felix.framework.Felix;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleException;
import org.osgi.framework.BundleListener;
import org.osgi.framework.Constants;
import org.osgi.framework.launch.Framework;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

/**
 * A bootstrapper for Interactive Spaces on an Android device.
 *
 * @author Keith M. Hughes
 */
public class InteractiveSpacesFrameworkAndroidBootstrap {

    /**
     * Extensions on config files.
     */
    private static final String CONFIGURATION_FILES_EXTENSION = ".conf";

    /**
     * Subdirectory which will contain the bootstrap bundles.
     */
    public static final String BUNDLE_DIRECTORY_BOOTSTRAP = "bootstrap";

    /**
     * Where the config files are stored.
     */
    public static final String CONFIG_DIRECTORY = "config";

    /**
     * The OSGI framework which has been started.
     */
    private Framework framework;

    /**
     * All bundles installed.
     */
    private Set<Bundle> bundles = new HashSet<Bundle>();

    /**
     * The initial set of bundles to load.
     */
    private List<String> initialBundles;

    /**
     * The final set of bundles to load.
     */
    private List<String> finalBundles;

    /**
     * Mapping of JAR files to the bundles they are once loaded.
     */
    private Map<String, Bundle> jarToBundle = new HashMap<String, Bundle>();

    /**
     * Activator for the base Interactive Spaces services.
     */
    private GeneralInteractiveSpacesSupportActivator isSystemActivator;

    /**
     * Logging provider for the container.
     */
    private AndroidLoggingProvider loggingProvider;

    /**
     * Configuration provider for the container.
     */
    private ConfigurationProvider configurationProvider;

    /**
     * Container customizer provider for the container.
     */
    private SimpleContainerCustomizerProvider containerCustomizerProvider;

    /**
     * Start the framework up.
     *
     * @param context
     *          the Android context to start the framework up in.
     */
    public void startup(Map<String, String> config, Context context) {
        AssetManager assetManager = context.getAssets();

        try {
            createCoreServices(context);

            copyInitialBootstrapAssets(assetManager, context.getFilesDir());

            getBootstrapBundleJars(assetManager, BUNDLE_DIRECTORY_BOOTSTRAP);

            // If no bundle JAR files are in the directory, then exit.
            if (initialBundles.isEmpty() && finalBundles.isEmpty()) {
                System.out.println("No bundles to install.");
            }

            createAndStartFramework(config, context);

            registerCoreServices();

            List<Bundle> bundleList = new ArrayList<Bundle>();

            // Install bundle JAR files and remember the bundle objects.
            BundleContext ctxt = framework.getBundleContext();
            ctxt.addBundleListener(new BundleListener() {

                @Override
                public void bundleChanged(BundleEvent event) {
                    bundleChangeEvent(event);
                }

            });

            isSystemActivator = new GeneralInteractiveSpacesSupportActivator();
            isSystemActivator.start(framework.getBundleContext());

            OsgiControllerActivator isControllerActivator = new OsgiControllerActivator();
            isControllerActivator.start(framework.getBundleContext());

            startBundles(assetManager, ctxt, bundleList, initialBundles, false);
            startBundles(assetManager, ctxt, bundleList, finalBundles, true);
        } catch (Exception ex) {
            System.err.println("Error starting framework: " + ex);
            ex.printStackTrace();
            System.exit(0);
        }
    }

    /**
     * Shut the controller down.
     */
    public void shutdown() {
        if (framework != null) {
            try {
                framework.stop();
            } catch (BundleException e) {
                Log.e("interactivespaces", "Exception during Interactive Spaces OSGi shutdown", e);
            }
            framework = null;
        }
    }

    /**
     * Create the core services to the base bundle which are platform dependent.
     *
     * @param context
     *          Android context for the service
     */
    public void createCoreServices(Context context) {
        loggingProvider = new AndroidLoggingProvider();
        configurationProvider = new AndroidConfigurationProvider(
                PreferenceManager.getDefaultSharedPreferences(context));

        containerCustomizerProvider = new SimpleContainerCustomizerProvider();
        containerCustomizerProvider.addService(AndroidOsService.SERVICE_NAME, new SimpleAndroidOsService(context));
    }

    /**
     * Register all bootstrap core services with the container.
     */
    public void registerCoreServices() {
        BundleContext bundleContext = framework.getBundleContext();
        bundleContext.registerService(LoggingProvider.class.getName(), loggingProvider, null);
        bundleContext.registerService(ConfigurationProvider.class.getName(), configurationProvider, null);
        bundleContext.registerService(ContainerCustomizerProvider.class.getName(), containerCustomizerProvider,
                null);
    }

    /**
     * An event happened on one of the bundles in the container.
     *
     * @param event
     *          the OSGi bundle event
     */
    private void bundleChangeEvent(BundleEvent event) {
        System.out.format("Bundle %s changed state to %d\n", event.getBundle().getLocation(),
                event.getBundle().getState());
    }

    /**
     * @param ctxt
     * @param bundleList
     * @param jars
     * @throws BundleException
     */
    private void startBundles(AssetManager assetManager, BundleContext ctxt, List<Bundle> bundleList,
            List<String> jars, boolean start) throws BundleException {
        for (String bundleFile : jars) {
            try {
                Bundle b = ctxt.installBundle(bundleFile, assetManager.open(bundleFile));

                bundleList.add(b);
                bundles.add(b);
                jarToBundle.put(bundleFile.substring(bundleFile.indexOf('/') + 1), b);
                System.out.format("Added bundle file %s with ID %d\n", bundleFile, b.getBundleId());
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        // Start all installed non-fragment bundles.
        for (final Bundle bundle : bundleList) {
            if (!isFragment(bundle)) {
                if (start)
                    startBundle(bundle);
            }
        }
    }

    /**
     * Start a particular bundle.
     *
     * @param bundle
     *          the bundle to start
     */
    private void startBundle(Bundle bundle) {
        boolean started = true;
        try {
            System.out.format("Starting %s\n", bundle.getLocation());
            bundle.start();
            System.out.format("Started %s\n", bundle.getLocation());
        } catch (Exception e) {
            started = false;
            System.err.println("Exception " + bundle.getLocation());
            e.printStackTrace();
        }
    }

    /**
     * Create, configure, and start an OSGi framework instance. OO
     *
     * @return
     * @throws IOException
     * @throws BundleException
     */
    private void createAndStartFramework(Map<String, String> bootstrapConfig, Context context)
            throws BundleException, IOException {
        Map<String, String> m = new HashMap<String, String>();

        File filesDir = context.getFilesDir();
        m.put("interactivespaces.rootdir", filesDir.getAbsolutePath());

        // m.putAll(System.getProperties());
        m.put(Constants.FRAMEWORK_STORAGE_CLEAN, Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT);

        String delegations = getClassloaderDelegations(filesDir);
        if (delegations != null) {
            m.put(Constants.FRAMEWORK_BOOTDELEGATION, delegations);
        }

        m.put(Constants.FRAMEWORK_SYSTEMPACKAGES, getSystemPackages());

        m.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, "log4j.properties");

        File file = new File(filesDir, "plugins-cache");
        file.mkdirs();
        m.put(Constants.FRAMEWORK_STORAGE, file.getCanonicalPath());

        m.put("felix.service.urlhandlers", "false");

        loadPropertyFiles(CONFIG_DIRECTORY, m);

        m.putAll(bootstrapConfig);

        framework = new Felix(m);
        framework.start();
    }

    /**
     * Get all system packages.
     *
     * @return an OSGi formatted list of system packages.
     */
    public String getSystemPackages() {
        List<String> packages = new ArrayList<String>();
        packages.add("org.osgi.framework; version=1.5");
        packages.add("org.osgi.service.event");
        packages.add("org.osgi.service.startlevel");
        packages.add("org.osgi.service.log");
        packages.add("org.osgi.util.tracker");
        packages.add("org.apache.felix.service.command");
        packages.add("org.osgi.service.packageadmin; version=1.2.0");
        packages.add("javax.xml");
        packages.add("javax.xml.xpath");
        packages.add("javax.xml.transform.sax");
        packages.add("javax.net");
        packages.add("javax.net.ssl");
        packages.add("javax.xml.bind");
        packages.add("javax.crypto");
        packages.add("javax.management");
        packages.add("javax.script");
        packages.add("javax.xml.datatype");
        packages.add("javax.xml.namespace");
        packages.add("javax.xml.parsers");
        packages.add("javax.crypto.spec");
        packages.add("javax.security.auth.callback");
        packages.add("interactivespaces.system.core.logging");
        packages.add("com.google.common.collect, interactivespaces, interactivespaces.activity");
        packages.add("interactivespaces.configuration");
        packages.add("interactivespaces.controller");
        packages.add("interactivespaces.controller.activity.installation");
        packages.add("interactivespaces.domain.basic");
        packages.add("interactivespaces.domain.basic.pojo");
        packages.add("interactivespaces.evaluation");
        packages.add("interactivespaces.master.server.remote.client");
        packages.add("interactivespaces.master.server.remote.client.ros");
        packages.add("interactivespaces.system");
        packages.add("interactivespaces.time");
        packages.add("org.apache.commons.logging; version=1.1.1");
        packages.add("org.apache.commons.logging.impl; version=1.1.1");
        packages.add("interactivespaces.activity.execution");
        packages.add("interactivespaces.activity.impl");
        packages.add("interactivespaces.activity.impl.binary");
        packages.add("interactivespaces.activity.impl.ros");
        packages.add("interactivespaces.activity.impl.web");
        packages.add("interactivespaces.activity.binary");
        packages.add("interactivespaces.activity.component");
        packages.add("interactivespaces.activity.component.ros");
        packages.add("interactivespaces.activity.component.web");
        packages.add("interactivespaces.event");
        packages.add("interactivespaces.event.trigger");
        packages.add("interactivespaces.util");
        packages.add("interactivespaces.util.concurrency");
        packages.add("interactivespaces.util.data");
        packages.add("interactivespaces.util.data.persist");
        packages.add("interactivespaces.util.io");
        packages.add("interactivespaces.util.process.restart");
        packages.add("interactivespaces.util.ros");
        packages.add("interactivespaces.util.uuid");
        packages.add("interactivespaces.util.web");
        packages.add("interactivespaces.service");
        packages.add("interactivespaces.service.web.server");
        packages.add("org.ros.osgi.common");
        packages.add("org.ros.node");
        packages.add("org.ros.node.topic");
        packages.add("org.ros.message");
        packages.add("org.ros.message.interactivespaces_msgs; version=0.0.0");
        packages.add("interactivespaces.service.androidos");
        packages.add("android.content");
        packages.add("android.hardware");
        packages.add("android.net");
        packages.add("android.util");

        StringBuilder pkgs = new StringBuilder();
        String separator = "";
        for (String p : packages) {
            pkgs.append(separator).append(p);
            separator = ", ";
        }

        return pkgs.toString();
    }

    /**
     * Get all of the bundles from the bootstrap folder.
     *
     * @param assetManager
     *          the android asset manager for the application
     * @param bootstrapFolderPath
     *          The folder where the bootstrap folders are stored.
     *
     * @throws IOException
     */
    private void getBootstrapBundleJars(AssetManager assetManager, String bootstrapFolderPath) throws IOException {
        // Look in the specified bundle directory to create a list
        // of all JAR files to install.
        initialBundles = new ArrayList<String>();
        finalBundles = new ArrayList<String>();

        for (String filename : assetManager.list(bootstrapFolderPath)) {
            if (filename.endsWith(".jar")) {
                if (filename.startsWith("interactivespaces")) {
                    finalBundles.add(bootstrapFolderPath + "/" + filename);
                } else {
                    initialBundles.add(bootstrapFolderPath + "/" + filename);
                }
            }
        }
    }

    /**
     * Is the bundle a fragment host?
     *
     * @param bundle
     * @return
     */
    private boolean isFragment(Bundle bundle) {
        return bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null;
    }

    /**
     * Load all config files from the configuration folder.
     *
     * @param configFolder
     *          the configuration
     * @param properties
     *          where the properties from the configurations will be placed
     */
    private void loadPropertyFiles(String configFolder, Map<String, String> properties) {
        // Look in the specified bundle directory to create a list
        // of all JAR files to install.
        File[] files = new File(configFolder).listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.toLowerCase().endsWith(CONFIGURATION_FILES_EXTENSION);
            }
        });
        if (files == null || files.length == 0) {
            System.err.format("Couldn't load config files from %s\n", configFolder);
            return;
        }

        for (File file : files) {
            Properties props = new Properties();
            try {
                props.load(new FileInputStream(file));
                for (Entry<Object, Object> p : props.entrySet()) {
                    properties.put((String) p.getKey(), (String) p.getValue());
                }
            } catch (IOException e) {
                System.err.format("Couldn't load config file %s\n", file);
            }
        }
    }

    private String getClassloaderDelegations(File filesDir) {
        File delegation = new File(filesDir, "lib/system/java/delegations.conf");
        if (delegation.exists()) {

            StringBuilder builder = new StringBuilder();
            String separator = "";

            BufferedReader reader = null;
            try {
                reader = new BufferedReader(new FileReader(delegation));

                String line;
                while ((line = reader.readLine()) != null) {
                    builder.append(separator).append(line);
                    separator = ",";
                }

                return builder.toString();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (reader != null)
                    try {
                        reader.close();
                    } catch (IOException e) {
                        // Don't care. Closing.
                    }
            }
        }

        return null;
    }

    /**
     * Copy all asserts needed for initial boot.
     *
     * @param assetManager
     *          the android asset manager for the application
     * @param filesDir
     *          the folder where items can be copied to
     */
    private void copyInitialBootstrapAssets(AssetManager assetManager, File filesDir) {
        try {
            File confDir = new File(filesDir, "config");
            confDir.mkdirs();
            File isConfigDir = new File(confDir, "interactivespaces");
            isConfigDir.mkdirs();
            File bootstrapDir = new File(filesDir, "bootstrap");
            bootstrapDir.mkdirs();
            File systemLibDir = new File(filesDir, "lib/system/java");
            systemLibDir.mkdirs();
            File logsDir = new File(filesDir, "logs");
            logsDir.mkdirs();
            File activitiesStagingDir = new File(filesDir, "controller/activities/staging");
            activitiesStagingDir.mkdirs();
            File activitiesInstalledDir = new File(filesDir, "controller/activities/installed");
            activitiesInstalledDir.mkdirs();

            copyAssetFile("config/container.conf", new File(confDir, "container.conf"), assetManager);

            String[] configFiles = assetManager.list("config/interactivespaces");
            for (String configFile : configFiles) {
                copyAssetFile("config/interactivespaces/" + configFile, new File(isConfigDir, configFile),
                        assetManager);
            }

            copyAssetFile("lib/system/java/log4j.properties", new File(systemLibDir, "log4j.properties"),
                    assetManager);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * Copy an asset file.
     *
     * @param inputStream
     *          the input stream for the asset
     * @param outputFile
     *          where the file should be copied
     * @param assetManager
     *          TODO
     */
    private void copyAssetFile(String input, File outputFile, AssetManager assetManager) {

        System.out.format("Copying %s to %s\n", input, outputFile);

        byte buf[] = new byte[1024];
        int len;
        FileOutputStream fos = null;
        InputStream inputStream = null;
        try {
            inputStream = assetManager.open(input);
            fos = new FileOutputStream(outputFile);

            while ((len = inputStream.read(buf)) != -1) {
                fos.write(buf, 0, len);
            }
            fos.flush();
        } catch (IOException e) {

        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    // Don't care
                }
            }

            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // Don't care
                }
            }
        }
    }
}