blob: b93e40adbc7376d2e961b95a9aa9debfc3b6a346 [file] [log] [blame]
/*
* Copyright (c) 2014-2016 Eike Stepper (Berlin, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eike Stepper - initial API and implementation
* Yatta Solutions - [466264] Enhance UX in simple installer
*/
package org.eclipse.oomph.setup.internal.installer;
import org.eclipse.oomph.internal.setup.SetupProperties;
import org.eclipse.oomph.jreinfo.JREManager;
import org.eclipse.oomph.p2.core.P2Util;
import org.eclipse.oomph.p2.core.ProfileTransaction.Resolution;
import org.eclipse.oomph.setup.internal.core.SetupContext;
import org.eclipse.oomph.setup.internal.core.util.SetupCoreUtil;
import org.eclipse.oomph.setup.ui.wizards.SetupWizard.SelectionMemento;
import org.eclipse.oomph.ui.ErrorDialog;
import org.eclipse.oomph.ui.UIUtil;
import org.eclipse.oomph.util.IOUtil;
import org.eclipse.oomph.util.OS;
import org.eclipse.oomph.util.OomphPlugin.Preference;
import org.eclipse.oomph.util.PropertiesUtil;
import org.eclipse.oomph.util.StringUtil;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.edit.ui.provider.ExtendedImageRegistry;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.core.UIServices;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.service.datalocation.Location;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.osgi.framework.Bundle;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author Eike Stepper
*/
public class InstallerApplication implements IApplication
{
public static final Integer EXIT_ERROR = 1;
private static final Preference PREF_MODE = SetupInstallerPlugin.INSTANCE.getConfigurationPreference("mode");
private static final boolean SKIP_COCOA_MENU = PropertiesUtil.isProperty("oomph.cocoa.menu.skip");
private Mode mode = Mode.SIMPLE;
protected Integer run(final IApplicationContext context) throws Exception
{
// This must come very early, before the first model is accessed, so that HTTPS can be authorized.
IProvisioningAgent agent = P2Util.getCurrentProvisioningAgent();
agent.registerService(UIServices.SERVICE_NAME, Installer.SERVICE_UI);
Location location = Platform.getInstanceLocation();
if (location != null)
{
Location configurationLocation = Platform.getConfigurationLocation();
if (configurationLocation != null)
{
URL configurationLocationURL = configurationLocation.getURL();
location.set(configurationLocationURL, false);
}
}
final InstallerUI[] installerDialog = { null };
Thread jreInitializer = new Thread("JRE Initializer")
{
@Override
public void run()
{
JREManager.INSTANCE.getJREs();
for (;;)
{
InstallerUI installerUI = installerDialog[0];
if (installerUI != null)
{
installerUI.refreshJREs();
break;
}
try
{
sleep(100);
}
catch (InterruptedException ex)
{
return;
}
}
}
};
jreInitializer.setDaemon(true);
jreInitializer.start();
String windowImages = context.getBrandingProperty("windowImages");
if (windowImages != null)
{
Bundle brandingBundle = context.getBrandingBundle();
if (brandingBundle != null)
{
List<Image> images = new ArrayList<Image>();
for (String windowImageValue : StringUtil.explode(windowImages, ","))
{
URI windowImageURI = getConfigurationResourceURI(windowImageValue);
if (windowImageURI.isRelative())
{
URL url = brandingBundle.getEntry(windowImageValue);
if (url == null)
{
continue;
}
windowImageURI = URI.createURI(url.toString());
}
images.add(ExtendedImageRegistry.INSTANCE.getImage(windowImageURI));
}
if (!images.isEmpty())
{
Window.setDefaultImages(images.toArray(new Image[images.size()]));
}
}
}
// Check if the file association is registered, prompt if not, and register if requested.
if (FileAssociationUtil.isCheckRegistration())
{
if (FileAssociationUtil.INSTANCE.canBeRegistered())
{
FileAssociationDialog dialog = new FileAssociationDialog();
if (dialog.open() == FileAssociationDialog.OK)
{
FileAssociationUtil.setCheckRegistration(dialog.isRemember());
if (dialog.isRegister())
{
FileAssociationUtil.INSTANCE.register();
}
}
}
}
boolean restarted = false;
File restarting = new File(SetupContext.CONFIGURATION_STATE_LOCATION_URI.appendSegment("restarting").toFileString());
SelectionMemento selectionMemento = null;
if (restarting.exists())
{
try
{
restarted = true;
selectionMemento = (SelectionMemento)IOUtil.readObject(restarting, getClass().getClassLoader());
}
catch (Throwable ex)
{
//$FALL-THROUGH$
}
finally
{
try
{
restarting.delete();
}
catch (Throwable ex)
{
//$FALL-THROUGH$
}
}
}
final Display display = Display.getDefault();
Display.setAppName(PropertiesUtil.getProductName());
handleCocoaMenu(display, installerDialog);
display.asyncExec(new Runnable()
{
public void run()
{
// End the splash screen once the dialog is up.
context.applicationRunning();
}
});
String modeName = PropertiesUtil.getProperty(SetupProperties.PROP_SETUP_INSTALLER_MODE);
if (modeName == null)
{
modeName = PREF_MODE.get(Mode.SIMPLE.name());
}
mode = Mode.valueOf(modeName.toUpperCase());
@SuppressWarnings("rawtypes")
Map arguments = context.getArguments();
String[] applicationArgs = arguments == null ? null : (String[])arguments.get("application.args");
Collection<? extends Resource> configurationResources = getConfigurationResources(applicationArgs);
for (;;)
{
if (selectionMemento == null)
{
selectionMemento = new SelectionMemento();
}
Installer installer = new Installer(selectionMemento);
if (mode == Mode.ADVANCED)
{
if (KeepInstallerUtil.canKeepInstaller())
{
Shell shell = new Shell(display);
if (MessageDialog.openQuestion(shell, PropertiesUtil.getProductName(),
"As an advanced user, do you want to keep the installer in a permanent location?"))
{
if (new KeepInstallerDialog(shell, true).open() == KeepInstallerDialog.OK)
{
return EXIT_OK;
}
}
}
installer.setConfigurationResources(configurationResources);
installerDialog[0] = new InstallerDialog(null, installer, restarted);
}
else
{
SimpleInstallerDialog dialog = new SimpleInstallerDialog(display, installer, restarted);
installer.setSimpleShell(dialog);
installerDialog[0] = dialog;
}
final int retcode = installerDialog[0].show();
if (retcode == InstallerUI.RETURN_SIMPLE)
{
setMode(Mode.SIMPLE);
selectionMemento = null;
continue;
}
if (retcode == InstallerUI.RETURN_ADVANCED)
{
setMode(Mode.ADVANCED);
selectionMemento = null;
configurationResources = installer.getConfigurationResources();
continue;
}
if (retcode == InstallerUI.RETURN_RESTART)
{
try
{
IOUtil.writeObject(restarting, selectionMemento);
}
catch (Throwable ex)
{
//$FALL-THROUGH$
}
String launcher = OS.getCurrentLauncher(false);
if (launcher != null)
{
try
{
// EXIT_RESTART often makes the new process come up behind other windows, so try a fresh native process first.
launch(launcher);
return EXIT_OK;
}
catch (Throwable ex)
{
//$FALL-THROUGH$
}
}
return EXIT_RESTART;
}
return EXIT_OK;
}
}
private void setMode(Mode mode)
{
this.mode = mode;
PREF_MODE.set(mode.name());
}
private Set<Resource> getConfigurationResources(String[] applicationArgs)
{
Set<Resource> resources = new HashSet<Resource>();
ResourceSet resourceSet = null;
for (String arg : applicationArgs)
{
URI uri;
try
{
uri = getConfigurationResourceURI(arg);
}
catch (Throwable ex)
{
SetupInstallerPlugin.INSTANCE.log(ex);
continue;
}
if (resourceSet == null)
{
resourceSet = SetupCoreUtil.createResourceSet();
}
Resource resource;
try
{
resource = resourceSet.getResource(uri, true);
}
catch (Throwable ex)
{
SetupInstallerPlugin.INSTANCE.log(ex);
continue;
}
if (resource != null)
{
resources.add(resource);
}
}
return resources;
}
private URI getConfigurationResourceURI(String arg)
{
try
{
File file = new File(arg);
if (file.isFile() && file.canRead())
{
return URI.createFileURI(arg);
}
}
catch (Throwable ex)
{
//$FALL-THROUGH$
}
return URI.createURI(arg);
}
private void handleCocoaMenu(final Display display, final InstallerUI[] installerDialog)
{
if (!SKIP_COCOA_MENU && Platform.WS_COCOA.equals(Platform.getWS()))
{
Runnable about = new Runnable()
{
public void run()
{
if (installerDialog[0] != null)
{
installerDialog[0].showAbout();
}
}
};
Runnable preferences = new Runnable()
{
public void run()
{
if (installerDialog[0] != null)
{
NetworkConnectionsDialog proxyPreferenceDialog = new NetworkConnectionsDialog(installerDialog[0].getShell());
proxyPreferenceDialog.open();
}
}
};
Runnable quit = new Runnable()
{
public void run()
{
if (installerDialog[0] != null)
{
display.dispose();
}
}
};
CocoaUtil.register(display, about, preferences, quit);
}
}
public Object start(IApplicationContext context) throws Exception
{
try
{
return run(context);
}
catch (Throwable t)
{
SetupInstallerPlugin.INSTANCE.log(t);
final AtomicInteger exitCode = new AtomicInteger(EXIT_ERROR);
ErrorDialog dialog = new ErrorDialog("Error", t, 0, 2, IDialogConstants.OK_LABEL, "Update", IDialogConstants.SHOW_DETAILS_LABEL)
{
@Override
protected void buttonPressed(int buttonId)
{
super.buttonPressed(buttonId);
if (buttonId == 1)
{
update();
}
}
private void update()
{
try
{
final Shell shell = getShell();
if (!MessageDialog.openQuestion(shell, "Emergency Update", "This is an emergency update. Continue?\n\n"
+ "To lower the risk of problems during this update it will be implied that you accept new licenses or unsigned content."))
{
return;
}
ProgressMonitorDialog dialog = new ProgressMonitorDialog(shell)
{
@Override
protected Point getInitialSize()
{
Point calculatedSize = super.getInitialSize();
if (calculatedSize.x < 800)
{
calculatedSize.x = 800;
}
return calculatedSize;
}
};
try
{
dialog.run(true, true, new IRunnableWithProgress()
{
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException
{
SubMonitor progress = SubMonitor.convert(monitor, 2);
try
{
Resolution resolution = SelfUpdate.resolve(null, progress.newChild(1));
if (resolution == null)
{
UIUtil.syncExec(new Runnable()
{
public void run()
{
MessageDialog.openInformation(shell, "Update", "No updates were found.");
}
});
return;
}
resolution.commit(progress.newChild(1));
exitCode.set(EXIT_RESTART);
}
catch (Throwable ex)
{
throw new InvocationTargetException(ex);
}
finally
{
progress.done();
}
}
});
}
catch (OperationCanceledException ex)
{
//$FALL-THROUGH$
}
catch (InvocationTargetException ex)
{
if (!(ex.getCause() instanceof OperationCanceledException))
{
throw ex;
}
}
}
catch (Throwable ex)
{
SetupInstallerPlugin.INSTANCE.log(ex);
ErrorDialog.open(ex);
}
}
};
dialog.open();
return exitCode.get();
}
}
public void stop()
{
// Do nothing.
}
static void launch(String launcher) throws IOException
{
String[] args = Platform.getCommandLineArgs();
String[] cmdarray = new String[1 + args.length];
cmdarray[0] = launcher;
System.arraycopy(args, 0, cmdarray, 1, args.length);
Runtime.getRuntime().exec(cmdarray);
}
/**
* @author Eike Stepper
*/
public static enum Mode
{
SIMPLE, ADVANCED;
}
}