org.wso2.carbon.bridge.EquinoxFrameworkLauncher.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.bridge.EquinoxFrameworkLauncher.java

Source

/*
 * Copyright 2004,2005 The Apache Software Foundation.
 *
 * 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 org.wso2.carbon.bridge;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandlerFactory;
import java.security.AllPermission;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * The FrameworkLauncher provides the logic to:
 * 1) init
 * 2) deploy
 * 3) start
 * 4) stop
 * 5) undeploy
 * 6) destroy
 * an instance of the OSGi framework.
 * These 6 methods are provided to help manage the lifecycle and are called from outside this
 * class by the BridgeServlet. To create an extended FrameworkLauncher over-ride these methods to allow
 * custom behaviour.
 */
public class EquinoxFrameworkLauncher implements FrameworkLauncher {
    private static final String WS_DELIM = " \t\n\r\f";
    protected static final String FILE_SCHEME = "file:";
    protected static final String FRAMEWORK_BUNDLE_NAME = "org.eclipse.osgi";
    protected static final String STARTER = "org.eclipse.core.runtime.adaptor.EclipseStarter";
    protected static final String FRAMEWORKPROPERTIES = "org.eclipse.osgi.framework.internal.core.FrameworkProperties";
    protected static final String NULL_IDENTIFIER = "@null";
    protected static final String OSGI_FRAMEWORK = "osgi.framework";
    protected static final String OSGI_INSTANCE_AREA = "osgi.instance.area";
    protected static final String OSGI_CONFIGURATION_AREA = "osgi.configuration.area";
    protected static final String OSGI_INSTALL_AREA = "osgi.install.area";
    protected static final String OSGI_FORCED_RESTART = "osgi.forcedRestart";
    protected static final String RESOURCE_BASE = "/WEB-INF/eclipse/";
    protected static final String LAUNCH_INI = "launch.ini";

    private static final String ENABLE_OSGI_CONSOLE = "osgiConsole";
    private static final String ENABLE_OSGI_DEBUG = "osgiDebugOptions";
    private static final String BUNDLE_CREATORS = "bundleCreators";

    private static final String APPLY_PATCHES = "applyPatches";
    private static final String BUNDLE_BACKUP_DIR = "patch0000";

    /**
     * Is the OSGi framework running?
     */
    private boolean isRunning;

    static final PermissionCollection allPermissions = new PermissionCollection() {
        private static final long serialVersionUID = 482874725021998286L;
        // The AllPermission permission
        Permission allPermission = new AllPermission();

        // A simple PermissionCollection that only has AllPermission
        public void add(Permission permission) {
            // do nothing
        }

        public boolean implies(Permission permission) {
            return true;
        }

        public Enumeration elements() {
            return new Enumeration() {
                int cur = 0;

                public boolean hasMoreElements() {
                    return cur < 1;
                }

                public Object nextElement() {
                    if (cur == 0) {
                        cur = 1;
                        return allPermission;
                    }
                    throw new NoSuchElementException();
                }
            };
        }
    };

    static {
        // We do this to ensure the anonymous Enumeration class in allPermissions is pre-loaded
        if (allPermissions.elements() == null) {
            throw new IllegalStateException();
        }
    }

    protected ServletConfig servletConfig;
    protected ServletContext context;
    private File platformDirectory;
    private ClassLoader frameworkContextClassLoader;
    private URLClassLoader frameworkClassLoader;

    public void init(ServletConfig servletConfig) {
        this.servletConfig = servletConfig;
        context = servletConfig.getServletContext();
        init();
    }

    /**
     * init is the first method called on the FrameworkLauncher and can be used for any initial setup.
     * The default behaviour is to do nothing.
     */
    public void init() {
        // do nothing for now
        System.setProperty(START_TIME, String.valueOf(System.currentTimeMillis()));
    }

    /**
     * destory is the last method called on the FrameworkLauncher and can be used for any final cleanup.
     * The default behaviour is to do nothing.
     */
    public void destroy() {
        // do nothing for now
    }

    /**
     * deploy is used to move the OSGi framework libraries into a location suitable for execution.
     * The default behaviour is to copy the contents of the webapps WEB-INF/eclipse directory
     * to the webapps temp directory.
     */
    public synchronized void deploy() {
        platformDirectory = getCarbonComponentRepo();

        File plugins = new File(platformDirectory, "plugins");
        File patchesDir = new File(platformDirectory, "patches");
        //
        File dropinsFolder = new File(platformDirectory, "dropins");
        //copying resources inside patches folder to the work area.
        //TODO Copying of patches should only be performed by the master node, in multiple instance case.
        String applyPatches = System.getProperty(APPLY_PATCHES);
        if (applyPatches != null) {
            try {
                applyPatches(patchesDir, plugins);
            } catch (IOException e) {
                context.log("Error occurred while applying patches", e);
            }
        }

        // copy create OSGi framework extension bundles

    }

    /**
     * undeploy is the reverse operation of deploy and removes the OSGi framework libraries from their
     * execution location. Typically this method will only be called if a manual undeploy is requested in the
     * ServletBridge.
     * By default, this method removes the OSGi install and also removes the workspace.
     */
    public synchronized void undeploy() {
        if (platformDirectory == null) {
            context.log("Undeploy unnecessary. - (not deployed)");
            return;
        }

        if (frameworkClassLoader != null) {
            throw new IllegalStateException("Could not undeploy Framework - (not stopped)");
        }

        deleteDirectory(new File(platformDirectory, "configuration"));
        deleteDirectory(new File(platformDirectory, "features"));
        deleteDirectory(new File(platformDirectory, "plugins"));
        deleteDirectory(new File(platformDirectory, "workspace"));
        deleteDirectory(new File(platformDirectory, "p2"));

        if (!new File(platformDirectory, ".eclipseproduct").delete()) {
            context.log("Failed to deleted the directory: .eclipseproduct");
        }

        if (!new File(platformDirectory, "artifacts.xml").delete()) {
            context.log("Failed to deleted the file: artifacts.xml");
        }

        if (!new File(platformDirectory, "eclipse.ini").delete()) {
            context.log("Failed to deleted the file: eclipse.ini");
        }
        platformDirectory = null;
    }

    /**
     * start is used to "start" a previously deployed OSGi framework
     * The default behaviour will read launcher.ini to create a set of initial properties and
     * use the "commandline" configuration parameter to create the equivalent command line arguments
     * available when starting Eclipse.
     */
    public synchronized void start() {
        platformDirectory = getCarbonComponentRepo();
        if (platformDirectory == null) {
            throw new IllegalStateException("Could not start the Framework - (not deployed)");
        }

        if (frameworkClassLoader != null) {
            context.log("Framework is already started");
            return;
        }
        Map<String, String> initialPropsMap = buildInitialPropertyMap();
        String[] args = getArgs();

        ClassLoader original = Thread.currentThread().getContextClassLoader();
        try {
            System.setProperty("osgi.framework.useSystemProperties", "false");
            frameworkClassLoader = new ChildFirstURLClassLoader(
                    new URL[] { new URL(initialPropsMap.get(OSGI_FRAMEWORK)) }, this.getClass().getClassLoader());
            Class clazz = frameworkClassLoader.loadClass(STARTER);

            Method setInitialProperties = clazz.getMethod("setInitialProperties", Map.class);
            setInitialProperties.invoke(null, initialPropsMap);

            registerRestartHandler(clazz);

            Method runMethod = clazz.getMethod("startup", String[].class, Runnable.class);
            runMethod.invoke(null, args, null);

            frameworkContextClassLoader = Thread.currentThread().getContextClassLoader();
            isRunning = true;
        } catch (InvocationTargetException ite) {
            Throwable t = ite.getTargetException();
            if (t == null) {
                t = ite;
            }
            context.log("Error while starting Framework", t);
            throw new RuntimeException(t.getMessage());
        } catch (Exception e) {
            context.log("Error while starting Framework", e);
            throw new RuntimeException(e.getMessage());
        } finally {
            Thread.currentThread().setContextClassLoader(original);
        }
    }

    /**
     * stop is used to "shutdown" the framework and make it avialable for garbage collection.
     * The default implementation also has special handling for Apache Commons Logging to "release" any
     * resources associated with the frameworkContextClassLoader.
     */
    public synchronized void stop() {
        isRunning = false;
        if (platformDirectory == null) {
            context.log("Shutdown unnecessary. (not deployed)");
            return;
        }

        if (frameworkClassLoader == null) {
            context.log("Framework is already shutdown");
            return;
        }

        ClassLoader original = Thread.currentThread().getContextClassLoader();
        try {
            Class clazz = frameworkClassLoader.loadClass(STARTER);
            Method method = clazz.getDeclaredMethod("shutdown", (Class[]) null);
            Thread.currentThread().setContextClassLoader(frameworkContextClassLoader);
            method.invoke(clazz, (Object[]) null);

            // Invalidate all existing Http Sessions
            HttpSessionManager.invalidateSessions();

            // ACL keys its loggers off of the ContextClassLoader which prevents GC without calling release.
            // This section explicitly calls release if ACL is used.
            try {
                clazz = this.getClass().getClassLoader().loadClass("org.apache.commons.logging.LogFactory");
                method = clazz.getDeclaredMethod("release", ClassLoader.class);
                method.invoke(clazz, frameworkContextClassLoader);
            } catch (ClassNotFoundException e) {
                // ignore, ACL is not being used
            }
        } catch (Exception e) {
            context.log("Error while stopping Framework", e);
            return;
        } finally {
            frameworkClassLoader = null;
            frameworkContextClassLoader = null;
            Thread.currentThread().setContextClassLoader(original);
        }
    }

    public boolean isRunning() {
        return isRunning;
    }

    /**
     * copyResource is a convenience method to recursively copy resources from the ServletContext to
     * an installation target. The default behaviour will create a directory if the resourcepath ends
     * in '/' and a file otherwise.
     *
     * @param resourcePath - The resource root path
     * @param target       - The root location where resources are to be copied
     */
    protected void copyResource(String resourcePath, File target) {
        if (resourcePath.endsWith("/")) {
            if (!target.mkdir()) {
                context.log("Fail to create the directory: " + target.getAbsolutePath());
            }
            Set paths = context.getResourcePaths(resourcePath);
            if (paths == null) {
                return;
            }
            for (Iterator it = paths.iterator(); it.hasNext();) {
                String path = (String) it.next();
                File newFile = new File(target, path.substring(resourcePath.length()));
                copyResource(path, newFile);
            }
        } else {
            try {
                if (target.createNewFile()) {
                    InputStream is = null;
                    OutputStream os = null;
                    try {
                        is = context.getResourceAsStream(resourcePath);
                        if (is == null) {
                            return;
                        }
                        os = new FileOutputStream(target);
                        byte[] buffer = new byte[8192];
                        int bytesRead = is.read(buffer);
                        while (bytesRead != -1) {
                            os.write(buffer, 0, bytesRead);
                            bytesRead = is.read(buffer);
                        }
                    } finally {
                        try {
                            is.close();
                        } catch (IOException e) {
                            context.log("Unable to close the InputStream " + e.getMessage(), e);
                        }

                        try {
                            if (os != null) {
                                os.close();
                            }
                        } catch (IOException e) {
                            context.log("Unable to close the OutputStream " + e.getMessage(), e);
                        }
                    }
                }
            } catch (IOException e) {
                context.log("Error copying resources", e);
            }
        }
    }

    /**
     * Extracts all the feature jars and zip files specified in the compressFilePath to target with
     * the exception of jar files extracted to target/features/jarfilename folder
     *
     * @param compressFilesPath - The path which the feature archives are present
     * @param target            - The location where they should be extracted to
     */
    protected void extractFeatures(String compressFilesPath, File target) {
        if (compressFilesPath.endsWith("/")) {
            if (!target.mkdir()) {
                context.log("Fail to create the directory: " + target.getAbsolutePath());
            }
            Set paths = context.getResourcePaths(compressFilesPath);
            if (paths == null) {
                return;
            }
            for (Object path1 : paths) {
                String path = (String) path1;
                extractFeatures(path, target);
            }
        } else {
            if (compressFilesPath.endsWith(".jar")) {
                File tmpJarFilePath = new File(compressFilesPath);
                String featureName = tmpJarFilePath.getName().substring(0, tmpJarFilePath.getName().length() - 4);
                File featureFolder = new File(
                        target.getPath() + File.separator + "features" + File.separator + featureName);
                extractResource(compressFilesPath, featureFolder);
            } else
                extractResource(compressFilesPath, target);
        }
    }

    /**
     * Extract an archive to the target location
     *
     * @param compressFilePath - Archive path
     * @param target           - Location to extract
     */
    protected void extractResource(String compressFilePath, File target) {
        try {
            byte[] buf = new byte[1024];
            ZipInputStream zipinputstream;
            ZipEntry zipentry;
            zipinputstream = new ZipInputStream(context.getResourceAsStream(compressFilePath));

            zipentry = zipinputstream.getNextEntry();
            while (zipentry != null) {
                String entryName = zipentry.getName();
                int n;
                FileOutputStream fileoutputstream;
                File newFile = new File(entryName);
                String directory = newFile.getParent();

                if (directory == null) {
                    if (newFile.isDirectory())
                        break;
                }

                if (zipentry.isDirectory()) {
                    zipinputstream.closeEntry();
                    zipentry = zipinputstream.getNextEntry();
                    continue;
                }

                if (newFile.isDirectory())
                    break;
                File outputFile = new File(target.getPath(), entryName);
                if (outputFile.getParentFile() != null && !outputFile.getParentFile().mkdirs()) {
                    throw new IOException(
                            "Fail to create the directory: " + outputFile.getParentFile().getAbsolutePath());
                }
                fileoutputstream = new FileOutputStream(outputFile);

                while ((n = zipinputstream.read(buf, 0, 1024)) > -1)
                    fileoutputstream.write(buf, 0, n);

                fileoutputstream.close();
                zipinputstream.closeEntry();
                zipentry = zipinputstream.getNextEntry();

            }

            zipinputstream.close();
        } catch (Exception ignored) {
            //ignore invalid compress file
            context.log(ignored.getMessage(), ignored);
        }
    }

    /**
     * deleteDirectory is a convenience method to recursively delete a directory
     *
     * @param directory - the directory to delete.
     * @return was the delete succesful
     */
    protected static boolean deleteDirectory(File directory) {
        if (directory.exists() && directory.isDirectory()) {
            File[] files = directory.listFiles();
            for (File file : files) {
                if (file.isDirectory()) {
                    deleteDirectory(file);
                } else {
                    if (!file.delete()) {
                        System.err.println("Fail to create the directory: " + file.getAbsolutePath());
                    }
                }
            }
        }
        return directory.delete();
    }

    /**
     * Used when to set the ContextClassLoader when the BridgeServlet delegates to a Servlet
     * inside the framework
     *
     * @return a Classloader with the OSGi framework's context classloader.
     */
    public synchronized ClassLoader getFrameworkContextClassLoader() {
        return frameworkContextClassLoader;
    }

    /**
     * Platfom Directory is where the OSGi software is installed
     *
     * @return the framework install location
     */
    protected synchronized File getPlatformDirectory() {
        return platformDirectory;
    }

    /**
     * loadProperties is a convenience method to load properties from a servlet context resource
     *
     * @param resource - The target to read properties from
     * @return the properties
     */
    protected Properties loadProperties(String resource) {
        Properties result = new Properties();
        InputStream in = null;
        try {
            URL location = context.getResource(resource);
            if (location != null) {
                in = location.openStream();
                result.load(in);
            }
        } catch (MalformedURLException e) {
            // no url to load from
        } catch (IOException e) {
            // its ok if there is no file
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    // ignore
                }
            }
        }
        return result;
    }

    /**
     * Searches for the given target directory starting in the "plugins" subdirectory
     * of the given location.  If one is found then this location is returned;
     * otherwise an exception is thrown.
     *
     * @param target target
     * @param start  the location to begin searching
     * @return the location where target directory was found
     */
    protected String searchFor(final String target, String start) {
        FileFilter filter = new FileFilter() {
            public boolean accept(File candidate) {
                return candidate.getName().equals(target) || candidate.getName().startsWith(target + "_");
            }
        };
        File[] candidates = new File(start).listFiles(filter);
        if (candidates == null) {
            return null;
        }
        String[] arrays = new String[candidates.length];
        for (int i = 0; i < arrays.length; i++) {
            arrays[i] = candidates[i].getName();
        }
        int result = findMax(arrays);
        if (result == -1) {
            return null;
        }
        return candidates[result].getAbsolutePath().replace(File.separatorChar, '/')
                + (candidates[result].isDirectory() ? "/" : "");
    }

    protected int findMax(String[] candidates) {
        int result = -1;
        Object maxVersion = null;
        for (int i = 0; i < candidates.length; i++) {
            String name = candidates[i];
            String version = ""; // Note: directory with version suffix is always > than directory without version suffix
            int index = name.indexOf('_');
            if (index != -1) {
                version = name.substring(index + 1);
            }
            Object currentVersion = getVersionElements(version);
            if (maxVersion == null) {
                result = i;
                maxVersion = currentVersion;
            } else {
                if (compareVersion((Object[]) maxVersion, (Object[]) currentVersion) < 0) {
                    result = i;
                    maxVersion = currentVersion;
                }
            }
        }
        return result;
    }

    /**
     * Compares version strings.
     *
     * @param left  left
     * @param right right
     * @return result of comparison, as integer;
     *         <code><0</code> if left < right;
     *         <code>0</code> if left == right;
     *         <code>>0</code> if left > right;
     */
    private int compareVersion(Object[] left, Object[] right) {

        int result = ((Integer) left[0]).compareTo((Integer) right[0]); // compare major
        if (result != 0) {
            return result;
        }

        result = ((Integer) left[1]).compareTo((Integer) right[1]); // compare minor
        if (result != 0) {
            return result;
        }

        result = ((Integer) left[2]).compareTo((Integer) right[2]); // compare service
        if (result != 0) {
            return result;
        }

        return ((String) left[3]).compareTo((String) right[3]); // compare qualifier
    }

    /**
     * Do a quick parse of version identifier so its elements can be correctly compared.
     * If we are unable to parse the full version, remaining elements are initialized
     * with suitable defaults.
     *
     * @param version Version
     * @return an array of size 4; first three elements are of type Integer (representing
     *         major, minor and service) and the fourth element is of type String (representing
     *         qualifier). Note, that returning anything else will cause exceptions in the caller.
     */
    private Object[] getVersionElements(String version) {
        if (version.endsWith(".jar")) {
            version = version.substring(0, version.length() - 4);
        }
        Object[] result = { 0, 0, 0, "" };
        StringTokenizer t = new StringTokenizer(version, ".");
        String token;
        int i = 0;
        while (t.hasMoreTokens() && i < 4) {
            token = t.nextToken();
            if (i < 3) {
                // major, minor or service ... numeric values
                try {
                    result[i++] = new Integer(token);
                } catch (Exception e) {
                    // invalid number format - use default numbers (0) for the rest
                    break;
                }
            } else {
                // qualifier ... string value
                result[i++] = token;
            }
        }
        return result;
    }

    /**
     * Here is the patch applying algorithm.
     * 1) Creates a patch0000 (if it does not exist) inside the patches directory. Backup all the bundles in the plugins
     * directory
     * 2) Then copy all the patchxxxx to the plugins folder.
     * @param patchesDir
     * @param pluginsDir
     * @throws IOException
     */
    protected void applyPatches(File patchesDir, File pluginsDir) throws IOException {
        File bundleBackupDir = new File(patchesDir, BUNDLE_BACKUP_DIR);
        if (!bundleBackupDir.exists()) {
            //We need backup the plugins in the components/repository/plugins folder.
            File[] plugins = pluginsDir.listFiles();
            for (File plugin : plugins) {
                BridgeUtils.copyFileToDir(plugin, bundleBackupDir);
            }
        }
        //Now lets apply patches.
        copyPatches(patchesDir, pluginsDir);
    }

    /**
     * The functionality is same as copyResources method. But copyPatches method copy all the *.jar files in the
     * /WEB-INF/patches folder to flat target folder. Folder structure within the /WEB-INF/patches folder is not reflected
     * in the target folder.
     *
     * @param source folder which contains the patches.
     * @param target target
     * @throws java.io.IOException
     */
    protected void copyPatches(File source, File target) throws IOException {
        if (source.isDirectory()) {
            //Sorting patch folders.
            File[] files = source.listFiles();
            Arrays.sort(files);
            for (File file : files) {
                copyPatches(file, target);
            }
        } else {
            BridgeUtils.copyFileToDir(source, target);
        }
    }

    private File getCarbonComponentRepo() {
        String carbonRepo = System.getenv("CARBON_REPOSITORY");
        if (carbonRepo == null) {
            carbonRepo = System.getProperty("carbon.repository");
        }
        if (carbonRepo == null) {
            carbonRepo = System.getProperty("carbon.home") + File.separator + "repository";
        }
        String carbonComponentsRepository = carbonRepo + File.separator + "components";
        File componentRepo = new File(carbonComponentsRepository);
        if (!componentRepo.exists() && !componentRepo.mkdirs()) {
            System.err.println("Fail to create the directory: " + componentRepo.getAbsolutePath());
        }
        return componentRepo;
    }

    private void registerRestartHandler(Class starterClazz) throws NoSuchMethodException, ClassNotFoundException,
            IllegalAccessException, InvocationTargetException {
        Method registerFrameworkShutdownHandler;
        try {
            registerFrameworkShutdownHandler = starterClazz.getDeclaredMethod("internalAddFrameworkShutdownHandler",
                    Runnable.class);
        } catch (NoSuchMethodException e) {
            // Ok. However we will not support restart events. Log this as info
            context.log(starterClazz.getName()
                    + " does not support setting a shutdown handler. Restart handling is disabled.");
            return;
        }
        if (!registerFrameworkShutdownHandler.isAccessible()) {
            registerFrameworkShutdownHandler.setAccessible(true);
        }
        Runnable restartHandler = createRestartHandler();
        registerFrameworkShutdownHandler.invoke(null, restartHandler);
    }

    private Runnable createRestartHandler() throws ClassNotFoundException, NoSuchMethodException {
        Class frameworkPropertiesClazz = frameworkClassLoader.loadClass(FRAMEWORKPROPERTIES);
        final Method getProperty = frameworkPropertiesClazz.getMethod("getProperty", String.class);
        Runnable restartHandler = new Runnable() {
            public void run() {
                try {
                    String forcedRestart = (String) getProperty.invoke(null, OSGI_FORCED_RESTART);
                    if (Boolean.valueOf(forcedRestart).booleanValue()) {
                        stop();
                        start();
                    }
                } catch (InvocationTargetException ite) {
                    Throwable t = ite.getTargetException();
                    if (t == null) {
                        t = ite;
                    }
                    throw new RuntimeException(t.getMessage());
                } catch (Exception e) {
                    throw new RuntimeException(e.getMessage());
                }
            }
        };
        return restartHandler;
    }

    /**
    * buildInitialPropertyMap create the initial set of properties from the contents of launch.ini
    * and for a few other properties necessary to launch defaults are supplied if not provided.
    * The value '@null' will set the map value to null.
    *
    * @return a map containing the initial properties
    */
    private Map<String, String> buildInitialPropertyMap() {
        Map<String, String> initialPropertyMap = new HashMap<String, String>();
        Properties launchProperties = loadProperties(RESOURCE_BASE + LAUNCH_INI);
        for (Object o : launchProperties.entrySet()) {
            Map.Entry entry = (Map.Entry) o;
            String key = (String) entry.getKey();
            String value = (String) entry.getValue();
            if (key.endsWith("*")) { //$NON-NLS-1$
                if (value.equals(NULL_IDENTIFIER)) {
                    clearPrefixedSystemProperties(key.substring(0, key.length() - 1), initialPropertyMap);
                }
            } else if (value.equals(NULL_IDENTIFIER)) {
                initialPropertyMap.put(key, null);
            } else {
                initialPropertyMap.put((String) entry.getKey(), (String) entry.getValue());
            }
        }

        try {
            // install.area if not specified
            if (initialPropertyMap.get(OSGI_INSTALL_AREA) == null) {
                initialPropertyMap.put(OSGI_INSTALL_AREA, platformDirectory.toURL().toExternalForm());
            }

            // configuration.area if not specified
            if (initialPropertyMap.get(OSGI_CONFIGURATION_AREA) == null) {
                File configurationDirectory = new File(platformDirectory, "configuration");
                if (!configurationDirectory.exists() && !configurationDirectory.mkdirs()) {
                    context.log("Fail to create the directory: " + configurationDirectory.getAbsolutePath());
                }
                initialPropertyMap.put(OSGI_CONFIGURATION_AREA, configurationDirectory.toURL().toExternalForm());
            }

            // instance.area if not specified
            if (initialPropertyMap.get(OSGI_INSTANCE_AREA) == null) {
                File workspaceDirectory = new File(platformDirectory, "workspace");
                if (!workspaceDirectory.exists() && !workspaceDirectory.mkdirs()) {
                    context.log("Failed to create the directory: " + workspaceDirectory.getAbsoluteFile());
                }
                initialPropertyMap.put(OSGI_INSTANCE_AREA, workspaceDirectory.toURL().toExternalForm());
            }

            // osgi.framework if not specified
            if (initialPropertyMap.get(OSGI_FRAMEWORK) == null) {
                // search for osgi.framework in osgi.install.area
                String installArea = initialPropertyMap.get(OSGI_INSTALL_AREA);

                // only support file type URLs for install area
                if (installArea.startsWith(FILE_SCHEME)) {
                    installArea = installArea.substring(FILE_SCHEME.length());
                }

                String path = new File(installArea, "plugins").toString();
                path = searchFor(FRAMEWORK_BUNDLE_NAME, path);
                if (path == null) {
                    throw new RuntimeException("Could not find framework");
                }

                initialPropertyMap.put(OSGI_FRAMEWORK, new File(path).toURL().toExternalForm());
            }
        } catch (MalformedURLException e) {
            throw new RuntimeException("Error establishing location");
        }

        return initialPropertyMap;
    }

    /**
     * clearPrefixedSystemProperties clears System Properties by writing null properties in
     * the targetPropertyMap that match a prefix
     *
     * @param prefix            prefix
     * @param targetPropertyMap targetPropertyMap
     */
    private static void clearPrefixedSystemProperties(String prefix, Map<String, String> targetPropertyMap) {
        for (Object o : System.getProperties().keySet()) {
            String propertyName = (String) o;
            if (propertyName.startsWith(prefix) && !targetPropertyMap.containsKey(propertyName)) {
                targetPropertyMap.put(propertyName, null);
            }
        }
    }

    /**
     * buildCommandLineArguments parses the commandline config parameter into a set of arguments
     *
     * @return an array of String containing the commandline arguments
     */
    private String[] getArgs() {
        List<String> args = new ArrayList<String>();

        // Enable osgi console
        // First try to get from the System property
        String enableOsgiConsole = System.getProperty(ENABLE_OSGI_CONSOLE);
        if (enableOsgiConsole == null) {
            // Next try to get it from the web.xml
            enableOsgiConsole = servletConfig.getInitParameter(ENABLE_OSGI_CONSOLE);
        } else {
            if (!enableOsgiConsole.toLowerCase().equals("true")) {
                try {
                    enableOsgiConsole = "-console " + String.valueOf(Integer.parseInt(enableOsgiConsole));
                } catch (NumberFormatException ignored) {
                    enableOsgiConsole = "-console";
                }
            } else {
                enableOsgiConsole = "-console";
            }
        }

        if (enableOsgiConsole != null) {
            StringTokenizer tokenizer = new StringTokenizer(enableOsgiConsole, WS_DELIM);
            while (tokenizer.hasMoreTokens()) {
                String arg = tokenizer.nextToken();
                if (arg.startsWith("\"")) {
                    if (arg.endsWith("\"")) {
                        if (arg.length() >= 2) {
                            // strip the beginning and ending quotes
                            arg = arg.substring(1, arg.length() - 1);
                        }
                    } else {
                        String remainingArg = tokenizer.nextToken("\"");
                        arg = arg.substring(1) + remainingArg;
                        // skip to next whitespace separated token
                        tokenizer.nextToken(WS_DELIM);
                    }
                } else if (arg.startsWith("'")) {
                    if (arg.endsWith("'")) {
                        if (arg.length() >= 2) {
                            // strip the beginning and ending quotes
                            arg = arg.substring(1, arg.length() - 1);
                        }
                    } else {
                        String remainingArg = tokenizer.nextToken("'");
                        arg = arg.substring(1) + remainingArg;
                        // skip to next whitespace separated token
                        tokenizer.nextToken(WS_DELIM);
                    }
                }
                args.add(arg);
            }
            System.out.println("OSGi console has been enabled with options: " + enableOsgiConsole);
        }

        // Enable osgi debug
        // First try to get from the System property
        String enableOsgiDebug = System.getProperty(ENABLE_OSGI_DEBUG);
        if (enableOsgiDebug == null) {
            // Next try to get it from the web.xml
            enableOsgiDebug = servletConfig.getInitParameter(ENABLE_OSGI_DEBUG);
        } else {
            if (enableOsgiDebug.toLowerCase().equals("true")) {
                enableOsgiDebug = "lib/core/WEB-INF/eclipse/osgi-debug.options"; // TODO: Can get context root from carbon.xml in the future
            }
        }

        if (enableOsgiDebug != null) {
            args.add("-debug");
            args.add(enableOsgiDebug);
            System.out.println("OSGi debugging has been enabled with options: " + enableOsgiDebug);
        }

        return args.toArray(new String[] {});
    }

    /**
     * The ChildFirstURLClassLoader alters regular ClassLoader delegation and will check the URLs
     * used in its initialization for matching classes before delegating to it's parent.
     * Sometimes also referred to as a ParentLastClassLoader
     */
    protected class ChildFirstURLClassLoader extends URLClassLoader {

        public ChildFirstURLClassLoader(URL[] urls) {
            super(urls);
        }

        public ChildFirstURLClassLoader(URL[] urls, ClassLoader parent) {
            super(urls, parent);
        }

        public ChildFirstURLClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
            super(urls, parent, factory);
        }

        public URL getResource(String name) {
            URL resource = findResource(name);
            if (resource == null) {
                ClassLoader parent = getParent();
                if (parent != null) {
                    resource = parent.getResource(name);
                }
            }
            return resource;
        }

        protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
            Class clazz = findLoadedClass(name);
            if (clazz == null) {
                try {
                    clazz = findClass(name);
                } catch (ClassNotFoundException e) {
                    ClassLoader parent = getParent();
                    if (parent != null) {
                        clazz = parent.loadClass(name);
                    } else {
                        clazz = getSystemClassLoader().loadClass(name);
                    }
                }
            }

            if (resolve) {
                resolveClass(clazz);
            }

            return clazz;
        }

        // we want to ensure that the framework has AllPermissions
        protected PermissionCollection getPermissions(CodeSource codesource) {
            return allPermissions;
        }
    }
}