package org.rubypeople.rdt.internal.ui.wizards; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.net.URLConnection; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.ui.progress.UIJob; import org.rubypeople.rdt.internal.ui.RubyPlugin; class DownloadRubyWizardPage extends WizardPage implements IWizardPage { private static final String RUBY_INSTALLER_EXE = "ruby-installer.exe"; //$NON-NLS-1$ private static final String RUBY_INSTALLER_URL = "http://rubyforge.org/frs/download.php/47082/ruby186-27_rc2.exe"; //$NON-NLS-1$ private static final int BUFFER_SIZE = 64 * 1024; // 64k private static final int READ_TIMEOUT = 30000; // 30 seconds timeout on reads private static final int CONNECT_TIMEOUT = 15000; private static final long SLEEP_TIME = 100; // sleep .1sec between reads protected boolean fInstalledProperly; private IWizardPage fNextPage; private Label downloadButton; private Image fEnabledImage; private Image fDisabledImage; private MouseAdapter downloadListener; protected DownloadRubyWizardPage() { super(""); //$NON-NLS-1$ setTitle(NewWizardMessages.DownloadRubyWizardPage_TTL); setDescription(NewWizardMessages.DownloadRubyWizardPage_MSG_Description); } public void createControl(Composite parent) { Composite main = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; main.setLayout(layout); main.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); Label label = new Label(main, SWT.WRAP); label.setText(NewWizardMessages.DownloadRubyWizardPage_MSG_Explanation_text); GridData data = new GridData(SWT.FILL, SWT.FILL, true, false); data.widthHint = 400; label.setLayoutData(data); downloadButton = new Label(main, SWT.None); downloadButton.setImage(getEnabledButtonImage()); downloadListener = new MouseAdapter() { @Override public void mouseDown(MouseEvent e) { Display.getCurrent().asyncExec(new Runnable() { public void run() { downloadButton.setImage(getDisabledButtonImage()); } }); downloadRuby(); downloadButton.removeMouseListener(this); } }; downloadButton.addMouseListener(downloadListener); GridData downloadButtonData = new GridData(SWT.CENTER, SWT.CENTER, true, true); downloadButton.setLayoutData(downloadButtonData); setControl(main); } private Image getEnabledButtonImage() { if (fEnabledImage == null) { fEnabledImage = RubyPlugin.imageDescriptorFromPlugin(RubyPlugin.PLUGIN_ID, "icons/full/install_ruby.png") .createImage(); } return fEnabledImage; } private Image getDisabledButtonImage() { if (fDisabledImage == null) { fDisabledImage = RubyPlugin.imageDescriptorFromPlugin(RubyPlugin.PLUGIN_ID, "icons/full/install_ruby_disabled.png").createImage(); } return fDisabledImage; } @Override public void dispose() { if (fEnabledImage != null) { fEnabledImage.dispose(); fEnabledImage = null; } if (fDisabledImage != null) { fDisabledImage.dispose(); fDisabledImage = null; } super.dispose(); } protected void downloadRuby() { try { getContainer().run(true, true, new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { if (monitor == null) monitor = new NullProgressMonitor(); download(monitor); if (monitor.isCanceled()) return; try { monitor.subTask("Running installer..."); Process p = Runtime.getRuntime().exec(getSaveLocation()); int installerExit = p.waitFor(); if (installerExit != 0) { UIJob job = new UIJob("") //$NON-NLS-1$ { @Override public IStatus runInUIThread(IProgressMonitor monitor) { setErrorMessage(NewWizardMessages.DownloadRubyWizardPage_ERR_Installer_exit_failure); return Status.OK_STATUS; } }; job.setSystem(true); job.schedule(); return; } fInstalledProperly = true; } catch (IOException e) { setErrorMessage(NewWizardMessages.DownloadRubyWizardPage_ERR_Launching_installer); } } private void download(IProgressMonitor monitor) { String fileURL = RUBY_INSTALLER_URL; InputStream inStream = null; OutputStream outStream = null; try { URLConnection connection = new URL(fileURL).openConnection(); connection.setDoOutput(false); connection.setDoInput(true); connection.setReadTimeout(READ_TIMEOUT); connection.setAllowUserInteraction(false); connection.setConnectTimeout(CONNECT_TIMEOUT); connection.setUseCaches(false); connection.setRequestProperty("Accept", //$NON-NLS-1$ "application/zip, application/octet-stream, *; q=.2, */*; q=.2"); //$NON-NLS-1$ connection.connect(); int length = connection.getContentLength(); if (length != -1) monitor.beginTask(NewWizardMessages.DownloadRubyWizardPage_LBL_Downloading_ruby_installer, length); inStream = connection.getInputStream(); outStream = new FileOutputStream(getSaveLocation()); // R E A D / W R I T E by chunks // we know length > 0 int chunkSize = (int) Math.min(BUFFER_SIZE, length); long chunks = length / chunkSize; int lastChunkSize = (int) (length % chunkSize); // code will work even when lastChunkSize = 0 or chunks = 0; byte[] ba = new byte[chunkSize]; for (long i = 0; i < chunks; i++) { if (monitor.isCanceled()) { return; } int bytesRead = readBytesBlocking(inStream, ba, 0, chunkSize, READ_TIMEOUT); if (bytesRead != chunkSize) { throw new IOException(); } outStream.write(ba); monitor.worked(bytesRead); } // R E A D / W R I T E last chunk, if any if (lastChunkSize > 0) { int bytesRead = readBytesBlocking(inStream, ba, 0 /* offset in ba */, lastChunkSize, READ_TIMEOUT); if (bytesRead != lastChunkSize) { throw new IOException(); } outStream.write(ba, 0/* offset */, lastChunkSize/* length */); } } catch (IOException e) { UIJob job = new UIJob("") //$NON-NLS-1$ { @Override public IStatus runInUIThread(IProgressMonitor monitor) { setErrorMessage(NewWizardMessages.DownloadRubyWizardPage_ERR_Downloading_ruby_installer); return Status.OK_STATUS; } }; job.setSystem(true); job.schedule(); return; } finally { try { if (inStream != null) inStream.close(); } catch (IOException e) { } try { if (outStream != null) outStream.close(); } catch (IOException e) { } } } private int readBytesBlocking(InputStream in, byte b[], int off, int len, int timeoutInMillis) throws IOException { int totalBytesRead = 0; int bytesRead; long whenToGiveUp = System.currentTimeMillis() + timeoutInMillis; while (totalBytesRead < len && (bytesRead = in.read(b, off + totalBytesRead, len - totalBytesRead)) >= 0) { if (bytesRead == 0) { try { if (System.currentTimeMillis() >= whenToGiveUp) { throw new IOException("timeout"); //$NON-NLS-1$ } // don't hammer the system and suck up all the CPU // beating a tight loop when there are no chars. // If this keeps up we may trigger a java.net.SocketTimeoutException exception. Thread.sleep(SLEEP_TIME); } catch (InterruptedException e) { } } else { totalBytesRead += bytesRead; whenToGiveUp = System.currentTimeMillis() + timeoutInMillis; } } return totalBytesRead; } }); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (fInstalledProperly) { setNextPage(new BrowseToInstalledRubyWizardPage("C:\\ruby")); //$NON-NLS-1$ setErrorMessage(null); getContainer().updateButtons(); // Automatically advance user to next page getContainer().showPage(fNextPage); } } protected void setNextPage(IWizardPage page) { fNextPage = page; if (fNextPage == null) return; fNextPage.setWizard(getWizard()); ((WizardPage) getWizard().getStartingPage()).setPageComplete(false); } protected String getSaveLocation() { String value = System.getProperty("user.home"); //$NON-NLS-1$ if (value != null && value.trim().length() > 0) { return value + File.separator + "Desktop" + File.separator + RUBY_INSTALLER_EXE; //$NON-NLS-1$ } return "C:" + File.separator + RUBY_INSTALLER_EXE; //$NON-NLS-1$ } @Override public IWizardPage getNextPage() { return fNextPage; } @Override public boolean isPageComplete() { return canFlipToNextPage(); } @Override public boolean canFlipToNextPage() { return fNextPage != null; } }