/*******************************************************************************
* Copyright (c) 2006-2007, Cloudsmith Inc.
* The code, documentation and other materials contained herein have been
* licensed under the Eclipse Public License - v 1.0 by the copyright holder
* listed above, as the Initial Contributor under such license. The text of
* such license is available at www.eclipse.org.
******************************************************************************/
package org.eclipse.buckminster.jnlp.p2;
import static org.eclipse.buckminster.jnlp.p2.MaterializationConstants.ERROR_CODE_MISSING_ARGUMENT_EXCEPTION;
import static org.eclipse.buckminster.jnlp.p2.MaterializationConstants.ERROR_CODE_REMOTE_IO_EXCEPTION;
import static org.eclipse.buckminster.jnlp.p2.MaterializationConstants.ERROR_CODE_RUNTIME_EXCEPTION;
import static org.eclipse.buckminster.jnlp.p2.MaterializationConstants.ERROR_HELP_URL;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.codec.binary.Base64;
import org.eclipse.buckminster.core.CorePlugin;
import org.eclipse.buckminster.core.helpers.BMProperties;
import org.eclipse.buckminster.jnlp.p2.ui.general.wizard.AdvancedWizardDialog;
import org.eclipse.buckminster.jnlp.p2.wizard.install.InstallWizard;
import org.eclipse.buckminster.runtime.BuckminsterException;
import org.eclipse.buckminster.runtime.BuckminsterPreferences;
import org.eclipse.buckminster.runtime.IOUtils;
import org.eclipse.buckminster.runtime.Logger;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.window.Window.IExceptionHandler;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
/**
* This class controls all aspects of the application's execution
*/
public class Application implements IApplication
{
public static final Integer OK_EXIT_CODE = new Integer(0);
// fake, however, exit code 1 opens ugly window once the application is closed
public static final Integer ERROR_EXIT_CODE = new Integer(0);
/**
* The wizard dialog width
*/
private static final int WIZARD_MIN_WIDTH = 550;
private static final int WIZARD_MAX_WIDTH = 750;
/**
* The wizard dialog height
*/
private static final int WIZARD_MIN_HEIGHT = 550;
private static final int WIZARD_MAX_HEIGHT = 750;
private static final long DEFAULT_POPUP_DELAY = 500;
/**
* String for synchronization with the bootstrap
*/
private String m_syncString = null;
private String m_errorURL = ERROR_HELP_URL;
private String m_supportEmail;
public Object start(IApplicationContext context) throws Exception
{
Object runArgs = context.getArguments().get(IApplicationContext.APPLICATION_ARGS);
String errorCode = null;
try
{
String configUrl = null;
Long popupAfter = null;
if(runArgs instanceof String[])
{
String[] args = (String[])runArgs;
for(int idx = 0; idx < args.length; ++idx)
{
String arg = args[idx];
if("-configURL".equals(arg))
{
if(++idx < args.length)
{
configUrl = args[idx];
if(configUrl != null)
{
configUrl = configUrl.trim();
if(configUrl.length() == 0)
configUrl = null;
}
}
}
else if("-syncString".equals(arg))
{
if(++idx < args.length)
{
m_syncString = args[idx];
if(m_syncString != null)
{
m_syncString = m_syncString.trim();
if(m_syncString.length() == 0)
m_syncString = null;
}
}
}
else if("-popupAfter".equals(arg))
{
if(++idx < args.length)
{
try
{
popupAfter = Long.valueOf(args[idx]);
}
catch(NumberFormatException e)
{
// popupAfter remains null
}
}
}
}
}
// We need to create a display first thing since many mechanisms
// depend on its presence.
//
Display.setAppName("Materializer");
Display display = Display.getDefault();
HelpLinkErrorDialog.setSyncString(m_syncString);
if(!Platform.getInstanceLocation().lock())
{
errorCode = MaterializationConstants.ERROR_CODE_ALREADY_RUNNING_EXCEPTION;
throw BuckminsterException.fromMessage("Materializer is already running");
}
BuckminsterPreferences.setLogLevelConsole(Logger.SILENT);
BuckminsterPreferences.setLogLevelEclipseLogger(Logger.DEBUG);
if(configUrl == null)
{
errorCode = ERROR_CODE_MISSING_ARGUMENT_EXCEPTION;
throw BuckminsterException.fromMessage("Missing required argument -configURL <URL to config properties>");
}
Map<String, String> properties = new HashMap<String, String>();
InputStream propStream = null;
try
{
URL propertiesURL = new URL(configUrl);
propStream = new BufferedInputStream(propertiesURL.openStream());
Map<String, String> allProperties = new BMProperties(propStream);
// Get rid of empty properties
for(Map.Entry<String, String> entry : allProperties.entrySet())
{
String value = entry.getValue();
if(!(value == null || value.trim().length() == 0))
properties.put(entry.getKey(), value);
}
}
catch(IOException e)
{
errorCode = ERROR_CODE_REMOTE_IO_EXCEPTION;
throw BuckminsterException.fromMessage(e, "Can not read materialization information");
}
finally
{
IOUtils.close(propStream);
}
m_errorURL = properties.get(MaterializationConstants.PROP_ERROR_URL);
if(m_errorURL == null)
m_errorURL = MaterializationConstants.ERROR_HELP_URL;
m_supportEmail = properties.get(MaterializationConstants.PROP_SUPPORT_EMAIL);
String errorMessage = properties.get(MaterializationConstants.PROP_ERROR_MESSAGE);
if(errorMessage != null)
{
errorCode = MaterializationConstants.ERROR_CODE_404_EXCEPTION;
throw BuckminsterException.fromMessage(new String(Base64.decodeBase64(errorMessage.getBytes()), "UTF-8")); //$NON-NLS-1$
}
try
{
// Create the wizard dialog and resize it.
//
final InstallWizard installWizard = new InstallWizard(properties);
m_errorURL = installWizard.getErrorURL();
AdvancedWizardDialog dialog = new AdvancedWizardDialog(installWizard, ~SWT.APPLICATION_MODAL);
dialog.create();
// General exception handler
Window.setExceptionHandler(new IExceptionHandler()
{
public void handleException(Throwable t)
{
if(t instanceof ThreadDeath)
{
// Don't catch ThreadDeath as this is a normal occurrence when
// the thread dies
throw (ThreadDeath)t;
}
IStatus status = BuckminsterException.wrap(t.getCause() != null
? t.getCause()
: t).getStatus();
CorePlugin.logWarningsAndErrors(status);
String localErrorCode;
String message;
boolean reportable;
if(t instanceof JNLPException)
{
JNLPException e = (JNLPException)t;
localErrorCode = e.getErrorCode();
message = e.getMessage();
reportable = e.isReportable();
}
else
{
localErrorCode = ERROR_CODE_RUNTIME_EXCEPTION;
message = "An unexpected error occurred.\n\nThis could be because of intermittent network problems.";
reportable = true;
}
HelpLinkErrorDialog.openError(null, installWizard.getWindowImage(),
MaterializationConstants.ERROR_WINDOW_TITLE, message, status, localErrorCode,
reportable, m_supportEmail, "Materialization Error");
}
});
final Shell shell = dialog.getShell();
shell.setSize(Math.min(Math.max(WIZARD_MIN_WIDTH, shell.getSize().x), WIZARD_MAX_WIDTH), Math.min(
Math.max(WIZARD_MIN_HEIGHT, shell.getSize().y), WIZARD_MAX_HEIGHT));
// when the shell is not started "ON TOP", it starts blinking
shell.addShellListener(new ShellAdapter()
{
private int m_cnt = 0;
@Override
public void shellActivated(ShellEvent e)
{
if(m_cnt == 0)
{
Display.getDefault().asyncExec(new Runnable()
{
public void run()
{
shell.forceActive();
}
});
m_cnt++;
}
}
});
try
{
if(popupAfter != null)
{
long popupDelay = popupAfter.longValue() - (new Date()).getTime();
if(popupDelay > 0)
Thread.sleep(popupDelay);
}
synchronizeWithBootstrap();
long popupDelay = DEFAULT_POPUP_DELAY;
String popupDelayString = properties.get(MaterializationConstants.PROP_POPUP_DELAY);
if(popupDelayString != null)
{
try
{
popupDelay = new Long(popupDelayString).longValue();
}
catch(Throwable e)
{
popupDelay = DEFAULT_POPUP_DELAY;
}
}
// need to wait a while until applet finishes
Thread.sleep(popupDelay);
dialog.open();
return OK_EXIT_CODE;
}
catch(Throwable e)
{
errorCode = ERROR_CODE_RUNTIME_EXCEPTION;
final String finalErrorCode = errorCode;
final IStatus status = BuckminsterException.wrap(e).getStatus();
CorePlugin.logWarningsAndErrors(status);
Display.getDefault().syncExec(new Runnable()
{
public void run()
{
HelpLinkErrorDialog.openError(null, null, MaterializationConstants.ERROR_WINDOW_TITLE,
"Materialization wizard failed", status, finalErrorCode, true, m_supportEmail,
"Materialization Error");
}
});
return ERROR_EXIT_CODE;
}
}
finally
{
display.dispose();
}
}
catch(Throwable e)
{
e.printStackTrace();
if(errorCode == null)
{
errorCode = ERROR_CODE_RUNTIME_EXCEPTION;
}
final String finalErrorCode = errorCode;
final IStatus status = BuckminsterException.wrap(e).getStatus();
CorePlugin.logWarningsAndErrors(status);
Display.getDefault().syncExec(new Runnable()
{
public void run()
{
HelpLinkErrorDialog.openError(null, null, MaterializationConstants.ERROR_WINDOW_TITLE,
"Materialization cannot be started", status, finalErrorCode, true, m_supportEmail,
"Materialization Error");
}
});
return ERROR_EXIT_CODE;
}
}
public void stop()
{
}
private void synchronizeWithBootstrap()
{
if(m_syncString != null)
System.out.println(m_syncString);
}
}