All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hudson.lifecycle.WindowsSlaveInstaller Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 *
 * Copyright (c) 2004-2011 Oracle Corporation.
 *
 * 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:
 *
 *    Inc., Kohsuke Kawaguchi, Winston Prakash
 *
 *
 *******************************************************************************/ 

package hudson.lifecycle;

import hudson.Launcher.LocalLauncher;
import hudson.Util;
import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.remoting.Engine;
import hudson.remoting.jnlp.MainDialog;
import hudson.remoting.jnlp.MainMenu;
import hudson.util.StreamTaskListener;
import org.eclipse.hudson.jna.NativeAccessException;
import org.eclipse.hudson.jna.NativeUtils;
import java.util.logging.Logger;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URL;
import java.util.logging.Level;

import static javax.swing.JOptionPane.*;

/**
 * @author Kohsuke Kawaguchi
 */
public class WindowsSlaveInstaller implements Callable, ActionListener {
     
    /**
     * Root directory of this slave. String, not File because the platform can
     * be different.
     */
    private final String rootDir;
    private transient Engine engine;
    private transient MainDialog dialog;
    
    private NativeUtils nativeUtils = NativeUtils.getInstance();

    public WindowsSlaveInstaller(String rootDir, NativeUtils nativeUtils) {
        this.rootDir = rootDir;
    }
    
    public WindowsSlaveInstaller(String rootDir) {
        this.rootDir = rootDir;
    }

    public Void call() {
        if (File.separatorChar == '/') {
            return null;    // not Windows
        }
        if (System.getProperty("hudson.showWindowsServiceInstallLink") == null) {
            return null;    // only show this when it makes sense, which is when we run from JNLP
        }
        dialog = MainDialog.get();
        if (dialog == null) {
            return null;    // can't find the main window. Maybe not running with GUI
        }
        // capture the engine
        engine = Engine.current();

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                MainMenu mainMenu = dialog.getMainMenu();
                JMenu m = mainMenu.getFileMenu();
                JMenuItem menu = new JMenuItem(Messages.WindowsInstallerLink_DisplayName(), KeyEvent.VK_W);
                menu.addActionListener(WindowsSlaveInstaller.this);
                m.add(menu);
                mainMenu.commit();
            }
        });

        return null;
    }

    /**
     * Invokes slave.exe with a SCM management command.
     *
     * 

If it fails in a way that indicates the presence of UAC, retry in an * UAC compatible manner. */ static int runElevated(File slaveExe, String command, TaskListener out, File pwd, NativeUtils nativeUtils) throws IOException, InterruptedException { try { return new LocalLauncher(out).launch().cmds(slaveExe, command).stdout(out).pwd(pwd).join(); } catch (IOException e) { if (e.getMessage().contains("CreateProcess") && e.getMessage().contains("=740")) { // fall through } else { throw e; } } String logFile = "redirect.log"; try { return nativeUtils.windowsExec(slaveExe, command, logFile, pwd); } catch (NativeAccessException ex) { Logger.getLogger(WindowsSlaveInstaller.class.getName()).log(Level.SEVERE, null, ex); return -1; } finally { FileInputStream fin = null; try { fin = new FileInputStream(new File(pwd, "redirect.log")); IOUtils.copy(fin, out.getLogger()); } finally { IOUtils.closeQuietly(fin); } } } /** * Called when the install menu is selected */ public void actionPerformed(ActionEvent e) { try { int r = JOptionPane.showConfirmDialog(dialog, Messages.WindowsSlaveInstaller_ConfirmInstallation(), Messages.WindowsInstallerLink_DisplayName(), OK_CANCEL_OPTION); if (r != JOptionPane.OK_OPTION) { return; } if (!nativeUtils.isDotNetInstalled(2, 0)) { JOptionPane.showMessageDialog(dialog, Messages.WindowsSlaveInstaller_DotNetRequired(), Messages.WindowsInstallerLink_DisplayName(), ERROR_MESSAGE); return; } final File dir = new File(rootDir); if (!dir.exists()) { JOptionPane.showMessageDialog(dialog, Messages.WindowsSlaveInstaller_RootFsDoesntExist(rootDir), Messages.WindowsInstallerLink_DisplayName(), ERROR_MESSAGE); return; } final File slaveExe = new File(dir, "hudson-slave.exe"); FileUtils.copyURLToFile(getClass().getResource("/windows-service/hudson.exe"), slaveExe); // write out the descriptor URL jnlp = new URL(engine.getHudsonUrl(), "computer/" + Util.rawEncode(engine.slaveName) + "/slave-agent.jnlp"); String xml = generateSlaveXml( generateServiceId(rootDir), System.getProperty("java.home") + "\\bin\\java.exe", "-jnlpUrl " + jnlp.toExternalForm()); FileUtils.writeStringToFile(new File(dir, "hudson-slave.xml"), xml, "UTF-8"); // copy slave.jar URL slaveJar = new URL(engine.getHudsonUrl(), "jnlpJars/remoting.jar"); File dstSlaveJar = new File(dir, "slave.jar").getCanonicalFile(); if (!dstSlaveJar.exists()) // perhaps slave.jar is already there? { FileUtils.copyURLToFile(slaveJar, dstSlaveJar); } // install as a service ByteArrayOutputStream baos = new ByteArrayOutputStream(); StreamTaskListener task = new StreamTaskListener(baos); r = runElevated(slaveExe, "install", task, dir, nativeUtils); if (r != 0) { JOptionPane.showMessageDialog( dialog, baos.toString(), "Error", ERROR_MESSAGE); return; } r = JOptionPane.showConfirmDialog(dialog, Messages.WindowsSlaveInstaller_InstallationSuccessful(), Messages.WindowsInstallerLink_DisplayName(), OK_CANCEL_OPTION); if (r != JOptionPane.OK_OPTION) { return; } // let the service start after we close our connection, to avoid conflicts Runtime.getRuntime().addShutdownHook(new Thread("service starter") { public void run() { try { StreamTaskListener task = StreamTaskListener.fromStdout(); int r = runElevated(slaveExe, "start", task, dir, nativeUtils); task.getLogger().println(r == 0 ? "Successfully started" : "start service failed. Exit code=" + r); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }); System.exit(0); } catch (Exception t) {// this runs as a JNLP app, so if we let an exeption go, we'll never find out why it failed StringWriter sw = new StringWriter(); t.printStackTrace(new PrintWriter(sw)); JOptionPane.showMessageDialog(dialog, sw.toString(), "Error", ERROR_MESSAGE); } } public static String generateServiceId(String slaveRoot) throws IOException { return "hudsonslave-" + slaveRoot.replace(':', '_').replace('\\', '_').replace('/', '_'); } public static String generateSlaveXml(String id, String java, String args) throws IOException { String xml = IOUtils.toString(WindowsSlaveInstaller.class.getResourceAsStream("/windows-service/hudson-slave.xml"), "UTF-8"); xml = xml.replace("@ID@", id); xml = xml.replace("@JAVA@", java); xml = xml.replace("@ARGS@", args); return xml; } private static final long serialVersionUID = 1L; }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy