diff options
4 files changed, 270 insertions, 0 deletions
diff --git a/bundles/org.eclipse.osgi.tests/plugin.xml b/bundles/org.eclipse.osgi.tests/plugin.xml index b27d6ae5d..f8453e146 100644 --- a/bundles/org.eclipse.osgi.tests/plugin.xml +++ b/bundles/org.eclipse.osgi.tests/plugin.xml @@ -95,4 +95,16 @@ </run> </application> </extension> + <extension + id="relaunchApp" + point="org.eclipse.core.runtime.applications"> + <application + cardinality="1" + thread="main" + visible="true"> + <run + class="org.eclipse.osgi.tests.appadmin.RelaunchApp"> + </run> + </application> + </extension> </plugin> diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/appadmin/AllTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/appadmin/AllTests.java index 37a0e7283..66f8ca602 100644 --- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/appadmin/AllTests.java +++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/appadmin/AllTests.java @@ -20,6 +20,7 @@ public class AllTests { public static Test suite() { TestSuite suite = new TestSuite(AllTests.class.getName()); suite.addTest(ApplicationAdminTest.suite()); + suite.addTest(ApplicationRelaunchTest.suite()); return suite; } } diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/appadmin/ApplicationRelaunchTest.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/appadmin/ApplicationRelaunchTest.java new file mode 100644 index 000000000..d91ba9af9 --- /dev/null +++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/appadmin/ApplicationRelaunchTest.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (c) 2007, 2021 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.osgi.tests.appadmin; + +import java.util.HashMap; +import junit.framework.Test; +import junit.framework.TestSuite; +import org.eclipse.core.tests.session.ConfigurationSessionTestSuite; +import org.eclipse.core.tests.session.SetupManager.SetupException; +import org.eclipse.osgi.tests.OSGiTest; +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.service.application.ApplicationDescriptor; +import org.osgi.service.application.ApplicationHandle; + +// This is for the most part a stripped down copy of ApplicationAdminTest. +public class ApplicationRelaunchTest extends OSGiTest { + public static final String testRunnerRelauncherApp = PI_OSGI_TESTS + ".relaunchApp"; //$NON-NLS-1$ + public static final String testResults = "test.results"; //$NON-NLS-1$ + public static final String SUCCESS = "success"; //$NON-NLS-1$ + public static final String simpleResults = "test.simpleResults"; //$NON-NLS-1$ + public static final String[] tests = new String[] { "testRelaunch" }; + private static final String PI_OSGI_SERVICES = "org.eclipse.osgi.services"; //$NON-NLS-1$ + private static final String PI_OSGI_UTIL = "org.eclipse.osgi.util"; + + public static Test suite() { + TestSuite suite = new TestSuite(ApplicationRelaunchTest.class.getName()); + + ConfigurationSessionTestSuite appAdminSessionTest = new ConfigurationSessionTestSuite(PI_OSGI_TESTS, ApplicationRelaunchTest.class.getName()); + String[] ids = ConfigurationSessionTestSuite.MINIMAL_BUNDLE_SET; + for (String id : ids) { + appAdminSessionTest.addBundle(id); + } + appAdminSessionTest.addBundle(PI_OSGI_UTIL); + appAdminSessionTest.addBundle(PI_OSGI_SERVICES); + appAdminSessionTest.addBundle(PI_OSGI_TESTS); + appAdminSessionTest.setApplicationId(testRunnerRelauncherApp); + try { + appAdminSessionTest.getSetup().setSystemProperty("eclipse.application.registerDescriptors", "true"); //$NON-NLS-1$//$NON-NLS-2$ + } catch (SetupException e) { + throw new RuntimeException(e); + } + // we add tests the hard way so we can control the order of the tests. + for (String test : tests) { + appAdminSessionTest.addTest(new ApplicationRelaunchTest(test)); + } + suite.addTest(appAdminSessionTest); + return suite; + } + + public ApplicationRelaunchTest(String name) { + super(name); + } + + private ApplicationDescriptor getApplication(String appName) { + try { + BundleContext context = getContext(); + assertNotNull("BundleContext is null!!", context); //$NON-NLS-1$ + Class appDescClass = ApplicationDescriptor.class; + assertNotNull("ApplicationDescriptor.class is null!!", appDescClass); //$NON-NLS-1$ + ServiceReference[] refs = context.getServiceReferences(appDescClass.getName(), "(" + ApplicationDescriptor.APPLICATION_PID + "=" + appName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + if (refs == null || refs.length == 0) { + refs = getContext().getServiceReferences(ApplicationDescriptor.class.getName(), null); + String availableApps = ""; //$NON-NLS-1$ + if (refs != null) { + for (int i = 0; i < refs.length; i++) { + availableApps += refs[i].getProperty(ApplicationDescriptor.APPLICATION_PID); + if (i < refs.length - 1) + availableApps += ","; //$NON-NLS-1$ + } + } + fail("Could not find app pid: " + appName + " available apps are: " + availableApps); //$NON-NLS-1$ //$NON-NLS-2$ + } + ApplicationDescriptor result = (ApplicationDescriptor) getContext().getService(refs[0]); + if (result != null) + getContext().ungetService(refs[0]); + else + fail("Could not get application descriptor service: " + appName); //$NON-NLS-1$ + return result; + } catch (InvalidSyntaxException e) { + fail("Could not create app filter", e); //$NON-NLS-1$ + } + return null; + } + + private HashMap getArguments() { + HashMap args = new HashMap(); + args.put(testResults, new HashMap()); + return args; + } + + public void testRelaunch() { + // this is the same as ApplicationAdminTest.testSimpleApp() (but launched + // through a different test runner app RelaunchApp which is the thing being + // tested) + ApplicationDescriptor app = getApplication(PI_OSGI_TESTS + ".simpleApp"); //$NON-NLS-1$ + HashMap args = getArguments(); + HashMap results = (HashMap) args.get(testResults); + try { + ApplicationHandle handle = app.launch(args); + handle.destroy(); + } catch (Throwable e) { + fail("failed to launch simpleApp", e); //$NON-NLS-1$ + } + String result = (String) results.get(simpleResults); + assertEquals("Check application result", SUCCESS, result); //$NON-NLS-1$ + } + +} diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/appadmin/RelaunchApp.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/appadmin/RelaunchApp.java new file mode 100644 index 000000000..f349f77d6 --- /dev/null +++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/appadmin/RelaunchApp.java @@ -0,0 +1,136 @@ +/******************************************************************************* +* Copyright (c) 2021 Indel AG and others. +* +* This program and the accompanying materials +* are made available under the terms of the Eclipse Public License 2.0 +* which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Indel AG - initial API and implementation +*******************************************************************************/ +package org.eclipse.osgi.tests.appadmin; + +import java.util.Collection; +import java.util.Map; +import org.eclipse.equinox.app.IApplication; +import org.eclipse.equinox.app.IApplicationContext; +import org.eclipse.osgi.service.environment.EnvironmentInfo; +import org.eclipse.osgi.tests.OSGiTestsActivator; +import org.osgi.framework.BundleException; +import org.osgi.framework.Constants; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.service.application.ApplicationDescriptor; +import org.osgi.service.application.ApplicationException; +import org.osgi.service.application.ApplicationHandle; + +public class RelaunchApp implements IApplication { + + @Override + public Object start(IApplicationContext context) throws Exception { + final Map arguments = context.getArguments(); + + // Setting eclipse.allowAppRelaunch to true at runtime should allow us to launch + // multiple applications in sequence + ServiceReference<EnvironmentInfo> envref = OSGiTestsActivator.getContext() + .getServiceReference(EnvironmentInfo.class); + EnvironmentInfo env = OSGiTestsActivator.getContext().getService(envref); + if (Boolean.valueOf(env.getProperty("eclipse.allowAppRelaunch"))) { //$NON-NLS-1$ + throw new AssertionError("eclipse.allowAppRelaunch should not be set initially"); //$NON-NLS-1$ + } + env.setProperty("eclipse.allowAppRelaunch", "true"); //$NON-NLS-1$ //$NON-NLS-2$ + OSGiTestsActivator.getContext().ungetService(envref); + + // Get a handle for the running application so we can wait for it to exit + ServiceReference<ApplicationHandle> thisAppRef = OSGiTestsActivator.getContext() + .getServiceReference(ApplicationHandle.class); + ApplicationHandle thisAppHandle = OSGiTestsActivator.getContext().getService(thisAppRef); + + new Thread("launcher") { //$NON-NLS-1$ + public void run() { + // Wait for this application to exit + try { + thisAppHandle.getExitValue(0); + } catch (ApplicationException e) { + // does not occur for timeout 0 + } catch (InterruptedException e) { + // I don't think this should occur + e.printStackTrace(); + } + + // Get the descriptor for the actual test runner application. + // Need a test runner that runs in the main thread to avoid race conditions. + Collection<ServiceReference<ApplicationDescriptor>> testAppRefs = null; + try { + testAppRefs = OSGiTestsActivator.getContext().getServiceReferences( + org.osgi.service.application.ApplicationDescriptor.class, + "(" + Constants.SERVICE_PID + "=org.eclipse.pde.junit.runtime.nonuithreadtestapplication)"); //$NON-NLS-1$ //$NON-NLS-2$ + } catch (InvalidSyntaxException e) { + // shouldn't happen, the hardcoded filter expression + // should be syntactically correct + e.printStackTrace(); + } + ServiceReference<ApplicationDescriptor> testAppRef = testAppRefs.iterator().next(); + ApplicationDescriptor testAppDescriptor = OSGiTestsActivator.getContext().getService(testAppRef); + + // Launch the new application + // If it does launch, it will run some unrelated succeeding test + // and thereby confirm that relaunching works. + try { + ApplicationHandle testAppHandle; + // There is a race condition in that the previous + // application may not have exited far enough yet for + // the EclipseAppLauncher to allow launching of a new + // application: Setting the exit value happens earlier + // in EclipseAppLauncher.runApplication() (inside + // EclipseAppHandle.run()) than releasing runningLock. + // Unfortunately there is no way to wait for the + // EclipseAppLauncher to be ready, so just try again + // after a delay when that happens. + while (true) { + try { + testAppHandle = testAppDescriptor.launch(arguments); + break; + } catch (IllegalStateException e) { + Thread.sleep(100); + } + } + + // Wait for the test application to exit + testAppHandle.getExitValue(0); + } catch (ApplicationException | InterruptedException e) { + // ApplicationException "The main thread is not available to launch the + // application" can happen when the test fails + e.printStackTrace(); + } finally { + OSGiTestsActivator.getContext().ungetService(thisAppRef); + OSGiTestsActivator.getContext().ungetService(testAppRef); + + try { + // This will not return but cause the process to terminate + OSGiTestsActivator.getContext().getBundle(0).stop(); + } catch (BundleException e) { + e.printStackTrace(); + } + } + } + }.start(); + + // If relaunching does not work, the process will end after this and the test + // will end with an error "Test did not run". The "launcher" thread will be + // killed wherever its execution happens to be, which is a race condition that + // means that there may be various exceptions printed or not. However even if it + // successfully got past testAppDescriptor.launch(), the test runner which wants + // to run in the main thread will never actually run, so the test cannot + // mistakenly succeed. + return null; + } + + @Override + public void stop() { + } + +} |