blob: f349f77d65fa0146ea0ba625c87526edebf8b858 [file] [log] [blame]
/*******************************************************************************
* 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() {
}
}