blob: 399f594274e17ce2574edad272bb52066fd40a58 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2018 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.equinox.internal.app;
import org.eclipse.osgi.framework.log.FrameworkLogEntry;
import org.eclipse.osgi.service.runnable.ApplicationRunnable;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.ServiceReference;
import org.osgi.service.application.ApplicationHandle;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
/**
* Listens for the default ApplicationHandle which run on any thread to be destroyed. This is used to force the main
* thread to wait while a default application runs on another thread.
*
* A main threaded application may be launched using this class to launch the main threaded application.
*/
public class DefaultApplicationListener implements ApplicationRunnable, ServiceTrackerCustomizer {
private boolean running = true; // indicates the default application is running
private EclipseAppHandle launchMainApp; // a handle to a main threaded application
private final ServiceTracker handleTracker; // tracks the default application handle
private Object result; // holds the result from the default application
public DefaultApplicationListener(EclipseAppHandle defaultApp) {
ServiceReference defaultRef = defaultApp.getServiceReference();
if (defaultRef == null) {
// service has been unregistered; application has ended already,
// save the result for latter
result = defaultApp.waitForResult(100);
handleTracker = null;
return;
}
ServiceTracker defaultAppTracker = new ServiceTracker(Activator.getContext(), defaultRef, this);
defaultAppTracker.open();
EclipseAppHandle trackedApp = (EclipseAppHandle) defaultAppTracker.getService();
if (trackedApp == null) {
// close tracker since we do not care about tracking the app (bug 215764)
defaultAppTracker.close();
// service has been unregistered; application has ended aready,
// save the result for latter
result = defaultApp.waitForResult(100);
handleTracker = null;
} else {
handleTracker = defaultAppTracker;
}
}
@Override
public Object run(Object context) {
if (handleTracker == null)
return getResult(); // app has ended, return the result
EclipseAppHandle anyThreadedDefaultApp = (EclipseAppHandle) handleTracker.getService();
if (anyThreadedDefaultApp != null)
// We now need to actual launch the application; this will run the application on another thread.
AnyThreadAppLauncher.launchEclipseApplication(anyThreadedDefaultApp);
try {
while (waitOnRunning()) {
EclipseAppHandle mainHandle = getMainHandle();
if (mainHandle != null) {
// while we were waiting for the default application to end someone asked for a main threaded app to launch
// note that we cannot hold the this lock while launching a main threaded application
try {
mainHandle.run(null);
} catch (Throwable e) {
String message = NLS.bind(Messages.application_error_starting, mainHandle.getInstanceId());
Activator.log(new FrameworkLogEntry(Activator.PI_APP, FrameworkLogEntry.WARNING, 0, message, 0, e, null));
}
unsetMainHandle(mainHandle);
}
}
} finally {
handleTracker.close();
}
return getResult();
}
private synchronized EclipseAppHandle getMainHandle() {
return launchMainApp;
}
private synchronized void unsetMainHandle(EclipseAppHandle mainHandle) {
if (launchMainApp == mainHandle)
launchMainApp = null;
}
private synchronized boolean waitOnRunning() {
if (!running)
return false;
try {
wait(100);
} catch (InterruptedException e) {
// do nothing
}
return running;
}
@Override
public void stop() {
if (handleTracker == null)
return;
// force the default application to quit
ApplicationHandle handle = (ApplicationHandle) handleTracker.getService();
if (handle != null) {
try {
handle.destroy();
} catch (Throwable t) {
String message = NLS.bind(Messages.application_error_stopping, handle.getInstanceId());
Activator.log(new FrameworkLogEntry(Activator.PI_APP, FrameworkLogEntry.WARNING, 0, message, 0, t, null));
}
}
}
@Override
public Object addingService(ServiceReference reference) {
return Activator.getContext().getService(reference);
}
@Override
public void modifiedService(ServiceReference reference, Object service) {
// do nothing
}
@Override
synchronized public void removedService(ServiceReference reference, Object service) {
running = false;
// only wait for 5 seconds; this may timeout if forcing an application to quit takes too long
// this should never timeout if the application exited normally.
result = ((EclipseAppHandle) service).waitForResult(5000);
EclipseAppHandle mainHandle = getMainHandle();
if (mainHandle != null)
// default application has quit; now force the main threaded application to quit
try {
mainHandle.destroy();
} catch (Throwable t) {
String message = NLS.bind(Messages.application_error_stopping, mainHandle.getInstanceId());
Activator.log(new FrameworkLogEntry(Activator.PI_APP, FrameworkLogEntry.WARNING, 0, message, 0, t, null));
}
this.notify();
}
synchronized void launch(EclipseAppHandle app) {
launchMainApp = app;
this.notify();
}
private synchronized Object getResult() {
return result;
}
}