package org.eclipse.equinox.internal.app;
import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.osgi.service.runnable.ApplicationRunnable;
import org.eclipse.osgi.service.runnable.StartupMonitor;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.*;
import org.osgi.service.application.ApplicationException;
import org.osgi.service.application.ApplicationHandle;
public class EclipseAppHandle extends ApplicationHandle implements ApplicationRunnable, IApplicationContext {
private static final int FLAG_STARTING = 0x01;
private static final int FLAG_ACTIVE = 0x02;
private static final int FLAG_STOPPING = 0x04;
private static final int FLAG_STOPPED = 0x08;
private static final String STARTING = "org.eclipse.equinox.app.starting";
private static final String STOPPED = "org.eclipse.equinox.app.stopped";
private static final String PROP_ECLIPSE_EXITCODE = "eclipse.exitcode";
private static final Object NULL_RESULT = new Object();
private volatile ServiceRegistration handleRegistration;
private int status = EclipseAppHandle.FLAG_STARTING;
private final Map<String, Object> arguments;
private Object application;
private final Boolean defaultAppInstance;
private Object result;
private boolean setResult = false;
private boolean setAsyncResult = false;
private final boolean[] registrationLock = new boolean[] {true};
EclipseAppHandle(String instanceId, Map<String, Object> arguments, EclipseAppDescriptor descriptor) {
super(instanceId, descriptor);
defaultAppInstance = arguments == null || arguments.get(EclipseAppDescriptor.APP_DEFAULT) == null ? Boolean.FALSE : (Boolean) arguments.remove(EclipseAppDescriptor.APP_DEFAULT);
if (arguments == null)
this.arguments = new HashMap<>(2);
else
this.arguments = new HashMap<>(arguments);
}
@Override
synchronized public String getState() {
switch (status) {
case FLAG_STARTING :
return STARTING;
case FLAG_ACTIVE :
return ApplicationHandle.RUNNING;
case FLAG_STOPPING :
return ApplicationHandle.STOPPING;
case FLAG_STOPPED :
default :
if (getServiceRegistration() == null)
throw new IllegalStateException(NLS.bind(Messages.application_error_state_stopped, getInstanceId()));
return STOPPED;
}
}
@Override
protected void destroySpecific() {
setAppStatus(EclipseAppHandle.FLAG_STOPPING);
IApplication app = getApplication();
if (app != null)
app.stop();
setAppStatus(EclipseAppHandle.FLAG_STOPPED);
}
void setServiceRegistration(ServiceRegistration sr) {
synchronized (registrationLock) {
this.handleRegistration = sr;
registrationLock[0] = sr != null;
registrationLock.notifyAll();
}
}
private ServiceRegistration getServiceRegistration() {
synchronized (registrationLock) {
if (handleRegistration == null && registrationLock[0]) {
try {
registrationLock.wait(1000);
} catch (InterruptedException e) {
}
}
return handleRegistration;
}
}
ServiceReference getServiceReference() {
ServiceRegistration reg = getServiceRegistration();
if (reg == null)
return null;
try {
return reg.getReference();
} catch (IllegalStateException e) {
return null;
}
}
Dictionary<String, Object> getServiceProperties() {
Dictionary<String, Object> props = new Hashtable<>(6);
props.put(ApplicationHandle.APPLICATION_PID, getInstanceId());
props.put(ApplicationHandle.APPLICATION_STATE, getState());
props.put(ApplicationHandle.APPLICATION_DESCRIPTOR, getApplicationDescriptor().getApplicationId());
props.put(EclipseAppDescriptor.APP_TYPE, ((EclipseAppDescriptor) getApplicationDescriptor()).getThreadTypeString());
props.put(ApplicationHandle.APPLICATION_SUPPORTS_EXITVALUE, Boolean.TRUE);
if (defaultAppInstance.booleanValue())
props.put(EclipseAppDescriptor.APP_DEFAULT, defaultAppInstance);
return props;
}
private synchronized void setAppStatus(int status) {
if (this.status == status)
return;
if ((status & EclipseAppHandle.FLAG_STARTING) != 0)
throw new IllegalArgumentException("Cannot set app status to starting");
if ((status & EclipseAppHandle.FLAG_STOPPING) != 0)
if ((this.status & (EclipseAppHandle.FLAG_STOPPING | EclipseAppHandle.FLAG_STOPPED)) != 0)
return;
this.status = status;
ServiceRegistration handleReg = getServiceRegistration();
if (handleReg == null)
return;
handleReg.setProperties(getServiceProperties());
if ((this.status & EclipseAppHandle.FLAG_STOPPED) != 0) {
((EclipseAppDescriptor) getApplicationDescriptor()).getContainerManager().unlock(this);
handleReg.unregister();
setServiceRegistration(null);
}
}
@Override
public Map getArguments() {
return arguments;
}
@Override
public Object run(Object context) throws Exception {
if (context != null) {
arguments.put(IApplicationContext.APPLICATION_ARGS, context);
} else {
context = arguments.get(IApplicationContext.APPLICATION_ARGS);
if (context == null) {
context = CommandLineArgs.getApplicationArgs();
arguments.put(IApplicationContext.APPLICATION_ARGS, context);
}
}
Object tempResult = null;
try {
Object app;
synchronized (this) {
if ((status & (EclipseAppHandle.FLAG_STARTING | EclipseAppHandle.FLAG_STOPPING)) == 0)
throw new ApplicationException(ApplicationException.APPLICATION_INTERNAL_ERROR, NLS.bind(Messages.application_instance_stopped, getInstanceId()));
application = getConfiguration().createExecutableExtension("run");
app = application;
notifyAll();
}
if (app instanceof IApplication)
tempResult = ((IApplication) app).start(this);
else
tempResult = EclipseAppContainer.callMethodWithException(app, "run", new Class[] {Object.class}, new Object[] {context});
if (tempResult == null)
tempResult = NULL_RESULT;
} finally {
tempResult = setInternalResult(tempResult, false, null);
}
if (Activator.DEBUG)
System.out.println(NLS.bind(Messages.application_returned, (new String[] {getApplicationDescriptor().getApplicationId(), tempResult == null ? "null" : tempResult.toString()})));
return tempResult;
}
private synchronized Object setInternalResult(Object result, boolean isAsync, IApplication tokenApp) {
if (setResult)
throw new IllegalStateException("The result of the application is already set.");
if (isAsync) {
if (!setAsyncResult)
throw new IllegalStateException("The application must return IApplicationContext.EXIT_ASYNC_RESULT to set asynchronous results.");
if (application != tokenApp)
throw new IllegalArgumentException("The application is not the correct instance for this application context.");
} else {
if (result == IApplicationContext.EXIT_ASYNC_RESULT) {
setAsyncResult = true;
return NULL_RESULT;
}
}
this.result = result;
setResult = true;
application = null;
notifyAll();
setAppStatus(EclipseAppHandle.FLAG_STOPPING);
setAppStatus(EclipseAppHandle.FLAG_STOPPED);
if (isDefault() && result != null) {
int exitCode = result instanceof Integer ? ((Integer) result).intValue() : 0;
Activator.setProperty(PROP_ECLIPSE_EXITCODE, Integer.toString(exitCode));
}
return result;
}
@Override
public void stop() {
try {
destroy();
} catch (IllegalStateException e) {
}
}
@Override
public void applicationRunning() {
setAppStatus(EclipseAppHandle.FLAG_ACTIVE);
final ServiceReference[] monitors = getStartupMonitors();
if (monitors == null)
return;
SafeRunner.run(new ISafeRunnable() {
@Override
public void handleException(Throwable e) {
}
@Override
public void run() throws Exception {
for (ServiceReference m : monitors) {
StartupMonitor monitor = (StartupMonitor) Activator.getContext().getService(m);
if (monitor != null) {
monitor.applicationRunning();
Activator.getContext().ungetService(m);
}
}
}
});
}
private ServiceReference[] getStartupMonitors() {
ServiceReference[] refs = null;
try {
refs = Activator.getContext().getServiceReferences(StartupMonitor.class.getName(), null);
} catch (InvalidSyntaxException e) {
}
if (refs == null || refs.length == 0)
return null;
Arrays.sort(refs, new Comparator<ServiceReference>() {
@Override
public int compare(ServiceReference ref1, ServiceReference ref2) {
Object property = ref1.getProperty(Constants.SERVICE_RANKING);
int rank1 = (property instanceof Integer) ? ((Integer) property).intValue() : 0;
property = ref2.getProperty(Constants.SERVICE_RANKING);
int rank2 = (property instanceof Integer) ? ((Integer) property).intValue() : 0;
if (rank1 != rank2)
return rank1 > rank2 ? -1 : 1;
long id1 = ((Long) (ref1.getProperty(Constants.SERVICE_ID))).longValue();
long id2 = ((Long) (ref2.getProperty(Constants.SERVICE_ID))).longValue();
return id2 > id1 ? -1 : 1;
}
});
return refs;
}
private synchronized IApplication getApplication() {
if (handleRegistration != null && application == null)
try {
wait(5000);
} catch (InterruptedException e) {
}
return (IApplication) ((application instanceof IApplication) ? application : null);
}
private IConfigurationElement getConfiguration() {
IExtension applicationExtension = ((EclipseAppDescriptor) getApplicationDescriptor()).getContainerManager().getAppExtension(getApplicationDescriptor().getApplicationId());
if (applicationExtension == null)
throw new RuntimeException(NLS.bind(Messages.application_notFound, getApplicationDescriptor().getApplicationId(), ((EclipseAppDescriptor) getApplicationDescriptor()).getContainerManager().getAvailableAppsMsg()));
IConfigurationElement[] configs = applicationExtension.getConfigurationElements();
if (configs.length == 0)
throw new RuntimeException(NLS.bind(Messages.application_invalidExtension, getApplicationDescriptor().getApplicationId()));
return configs[0];
}
@Override
public String getBrandingApplication() {
IBranding branding = ((EclipseAppDescriptor) getApplicationDescriptor()).getContainerManager().getBranding();
return branding == null ? null : branding.getApplication();
}
@Override
public Bundle getBrandingBundle() {
IBranding branding = ((EclipseAppDescriptor) getApplicationDescriptor()).getContainerManager().getBranding();
return branding == null ? null : branding.getDefiningBundle();
}
@Override
public String getBrandingDescription() {
IBranding branding = ((EclipseAppDescriptor) getApplicationDescriptor()).getContainerManager().getBranding();
return branding == null ? null : branding.getDescription();
}
@Override
public String getBrandingId() {
IBranding branding = ((EclipseAppDescriptor) getApplicationDescriptor()).getContainerManager().getBranding();
return branding == null ? null : branding.getId();
}
@Override
public String getBrandingName() {
IBranding branding = ((EclipseAppDescriptor) getApplicationDescriptor()).getContainerManager().getBranding();
return branding == null ? null : branding.getName();
}
@Override
public String getBrandingProperty(String key) {
IBranding branding = ((EclipseAppDescriptor) getApplicationDescriptor()).getContainerManager().getBranding();
return branding == null ? null : branding.getProperty(key);
}
boolean isDefault() {
return defaultAppInstance.booleanValue();
}
public synchronized Object waitForResult(int timeout) {
try {
return getExitValue(timeout);
} catch (ApplicationException | InterruptedException e) {
}
return null;
}
@Override
public synchronized Object getExitValue(long timeout) throws ApplicationException, InterruptedException {
if (handleRegistration == null && application == null)
return result;
long startTime = System.currentTimeMillis();
long delay = timeout;
while (!setResult && (delay > 0 || timeout == 0)) {
wait(delay);
if (timeout > 0)
delay -= (System.currentTimeMillis() - startTime);
}
if (result == null)
throw new ApplicationException(ApplicationException.APPLICATION_EXITVALUE_NOT_AVAILABLE);
if (result == NULL_RESULT)
return null;
return result;
}
@Override
public void setResult(Object result, IApplication application) {
setInternalResult(result == null ? NULL_RESULT : result, true, application);
}
}