Package org.ops4j.pax.exam.forked

Source Code of org.ops4j.pax.exam.forked.ForkedFrameworkFactory

/*
* Copyright 2011 Harald Wellmann.
*
* 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.ops4j.pax.exam.forked;

import java.io.File;
import java.net.URISyntaxException;
import java.rmi.NoSuchObjectException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.ops4j.exec.DefaultJavaRunner;
import org.ops4j.exec.ExecutionException;
import org.ops4j.net.FreePort;
import org.ops4j.pax.exam.TestContainerException;
import org.ops4j.pax.swissbox.framework.RemoteFramework;
import org.ops4j.pax.swissbox.framework.RemoteFrameworkImpl;
import org.ops4j.pax.swissbox.tracker.ServiceLookup;
import org.osgi.framework.launch.FrameworkFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Wraps an OSGi {@link FrameworkFactory} to create and launch a framework in a forked Java virtual
* machine running in a separate process.
* <p>
* The framework in the forked process can be controlled via a {@link RemoteFramework} interface.
*
* @author Harald Wellmann
*
*/
public class ForkedFrameworkFactory {

    private static final Logger LOG = LoggerFactory.getLogger(ForkedFrameworkFactory.class);

    // TODO make this configurable
    private static final long TIMEOUT = 60 * 1000;

    private FrameworkFactory frameworkFactory;
    private Registry registry;

    private int port;

    private DefaultJavaRunner javaRunner;

    /**
     * Creates a ForkedFrameworkFactory wrapping a given OSGi FrameworkFactory and a given framework
     * storage directory
     *
     * @param frameworkFactory
     *            OSGi framework factory
     */
    public ForkedFrameworkFactory(FrameworkFactory frameworkFactory) {
        this.frameworkFactory = frameworkFactory;
    }

    public FrameworkFactory getFrameworkFactory() {
        return frameworkFactory;
    }

    public void setFrameworkFactory(FrameworkFactory frameworkFactory) {
        this.frameworkFactory = frameworkFactory;
    }

    /**
     * Forks a Java VM process running an OSGi framework and returns a {@link RemoteFramework}
     * handle to it.
     * <p>
     *
     * @param vmArgs
     *            VM arguments
     * @param systemProperties
     *            system properties for the forked Java VM
     * @param frameworkProperties
     *            framework properties for the remote framework
     * @param beforeFrameworkClasspath
     *            system classpath entries before the framework itself
     * @param afterFrameworkClasspath
     *            system classpath entries after the framework itself
     * @return remote framework
     */
    public RemoteFramework fork(List<String> vmArgs, Map<String, String> systemProperties,
        Map<String, Object> frameworkProperties, List<String> beforeFrameworkClasspath,
        List<String> afterFrameworkClasspath) {
        // TODO make port range configurable
        FreePort freePort = new FreePort(21000, 21099);
        port = freePort.getPort();
        LOG.debug("using RMI registry at port {}", port);

        String rmiName = "ExamRemoteFramework-" + UUID.randomUUID().toString();

        try {
            registry = LocateRegistry.createRegistry(port);

            Map<String, String> systemPropsNew = new HashMap<String, String>(systemProperties);
            systemPropsNew.put(RemoteFramework.RMI_PORT_KEY, Integer.toString(port));
            systemPropsNew.put(RemoteFramework.RMI_NAME_KEY, rmiName);
            String[] vmOptions = buildSystemProperties(vmArgs, systemPropsNew);
            String[] args = buildFrameworkProperties(frameworkProperties);
            javaRunner = new DefaultJavaRunner(false);
            javaRunner.exec(vmOptions, buildClasspath(beforeFrameworkClasspath, afterFrameworkClasspath),
                RemoteFrameworkImpl.class.getName(), args, getJavaHome(), null);
            return findRemoteFramework(port, rmiName);
        }
        catch (RemoteException | ExecutionException | URISyntaxException exc) {
            throw new TestContainerException(exc);
        }
    }

    /**
     * Forks a Java VM process running an OSGi framework and returns a {@link RemoteFramework}
     * handle to it.
     * <p>
     *
     * @param vmArgs
     *            VM arguments
     * @param systemProperties
     *            system properties for the forked Java VM
     * @param frameworkProperties
     *            framework properties for the remote framework
     * @return remote framework
     */
    public RemoteFramework fork(List<String> vmArgs, Map<String, String> systemProperties,
        Map<String, Object> frameworkProperties) {
        return fork(vmArgs, systemProperties, frameworkProperties, null, null);
    }

    private String[] buildSystemProperties(List<String> vmArgs, Map<String, String> systemProperties) {
        String[] vmOptions = new String[vmArgs.size() + systemProperties.size()];
        int i = 0;
        for (String vmArg : vmArgs) {
            vmOptions[i++] = vmArg;
        }
        for (Map.Entry<String, String> entry : systemProperties.entrySet()) {
            vmOptions[i++] = String.format("-D%s=%s", entry.getKey(), entry.getValue());
        }
        return vmOptions;
    }

    private String[] buildFrameworkProperties(Map<String, Object> frameworkProperties) {
        String[] args = new String[frameworkProperties.size()];
        int i = 0;
        for (Map.Entry<String, Object> entry : frameworkProperties.entrySet()) {
            args[i++] = String.format("-F%s=%s", entry.getKey(), entry.getValue().toString());
        }
        return args;
    }

    private String getJavaHome() {
        String javaHome = System.getenv("JAVA_HOME");
        if (javaHome == null) {
            javaHome = System.getProperty("java.home");
        }
        return javaHome;
    }

    private String[] buildClasspath(List<String> beforeFrameworkClasspath,
        List<String> afterFrameworkClasspath) throws URISyntaxException {
        String frameworkPath = toPath(frameworkFactory.getClass());
        String launcherPath = toPath(RemoteFrameworkImpl.class);
        String serviceLookupPath = toPath(ServiceLookup.class);

        int entries = (beforeFrameworkClasspath != null ? beforeFrameworkClasspath.size() : 0)
            + 3 + (afterFrameworkClasspath != null ? afterFrameworkClasspath.size() : 0);
        String[] classpath = new String[entries];
        int i = 0;
        if (beforeFrameworkClasspath != null) {
            for (String beforeFrameworkLibrary : beforeFrameworkClasspath) {
                if (!new File(beforeFrameworkLibrary).exists()) {
                    throw new TestContainerException(
                            "Invalid boot classpath library: " + beforeFrameworkLibrary);
                }
                classpath[i++] = beforeFrameworkLibrary;
            }
        }
        classpath[i++] = frameworkPath;
        classpath[i++] = launcherPath;
        classpath[i++] = serviceLookupPath;
        if (afterFrameworkClasspath != null) {
            for (String afterFrameworkLibrary : afterFrameworkClasspath) {
                if (!new File(afterFrameworkLibrary).exists()) {
                    throw new TestContainerException(
                            "Invalid boot classpath library: " + afterFrameworkLibrary);
                }
                classpath[i++] = afterFrameworkLibrary;
            }
        }
        return classpath;
    }

    static String toPath(Class<?> klass) throws URISyntaxException {
        return klass.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
    }

    private RemoteFramework findRemoteFramework(int _port, String rmiName) {
        RemoteFramework framework = null;
        Throwable reason = null;
        long startedTrying = System.currentTimeMillis();

        do {
            try {
                Registry reg = LocateRegistry.getRegistry(_port);
                framework = (RemoteFramework) reg.lookup(rmiName);
            }
            catch (RemoteException e) {
                reason = e;
            }
            catch (NotBoundException e) {
                reason = e;
            }
        }
        while (framework == null && (System.currentTimeMillis() < startedTrying + TIMEOUT));
        if (framework == null) {
            throw new TestContainerException("cannot find remote framework in RMI registry", reason);
        }
        return framework;

    }

    /**
     * Waits for the remote framework to shutdown and frees all resources.
     */
    public void join() {
        try {
            UnicastRemoteObject.unexportObject(registry, true);
            /*
             * NOTE: javaRunner.waitForExit() works for Equinox and Felix, but not for Knopflerfish,
             * need to investigate why. OTOH, it may be better to kill the process as we're doing
             * now, just to be on the safe side.
             */
            javaRunner.shutdown();
        }
        catch (NoSuchObjectException exc) {
            throw new TestContainerException(exc);
        }
    }
}
TOP

Related Classes of org.ops4j.pax.exam.forked.ForkedFrameworkFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.