提交 0f9b5f62 编写于 作者: K kohsuke

Merged revisions 15420-15422,15540-15541,15556,15559-15560,15577 via svnmerge from

https://www.dev.java.net/svn/hudson/branches/managed-windows-slave

........
  r15420 | kohsuke | 2009-02-17 22:54:43 -0800 (Tue, 17 Feb 2009) | 1 line
  
  added a work in progress
........
  r15421 | kohsuke | 2009-02-17 23:01:47 -0800 (Tue, 17 Feb 2009) | 1 line
  
  added copyright
........
  r15422 | kohsuke | 2009-02-17 23:07:50 -0800 (Tue, 17 Feb 2009) | 1 line
  
  forgot to copy this
........
  r15540 | kohsuke | 2009-02-21 21:00:29 -0800 (Sat, 21 Feb 2009) | 1 line
  
  moving WMI support into another library, and making more progress
........
  r15541 | kohsuke | 2009-02-21 22:19:43 -0800 (Sat, 21 Feb 2009) | 3 lines
  
  making more progress.
  
  We need an UI to reinstall a service
........
  r15556 | kohsuke | 2009-02-22 11:15:12 -0800 (Sun, 22 Feb 2009) | 1 line
  
  allows null
........
  r15559 | kohsuke | 2009-02-22 11:29:08 -0800 (Sun, 22 Feb 2009) | 1 line
  
  added a new mode of launcher that accepts one TCP/IP connection and use that for communication
........
  r15560 | kohsuke | 2009-02-22 11:39:52 -0800 (Sun, 22 Feb 2009) | 1 line
  
  more bug fixes and stabilization
........
  r15577 | kohsuke | 2009-02-22 18:15:51 -0800 (Sun, 22 Feb 2009) | 2 lines
  
  improved the form field databinding by allowing @field to be on <f:entry>.
  This enables the convention-over-configuration for pointing to the help file.
........


git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@15579 71c3de6d-444a-0410-be80-ed276b4c234a
上级 375b16d2
......@@ -366,7 +366,7 @@ THE SOFTWARE.
<dependency>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>stapler</artifactId>
<version>1.94</version>
<version>1.96</version>
</dependency>
<dependency>
<groupId>org.jvnet.localizer</groupId>
......@@ -551,7 +551,7 @@ THE SOFTWARE.
<dependency>
<groupId>org.jvnet.hudson</groupId>
<artifactId>commons-jelly</artifactId>
<version>1.1-hudson-20080826</version>
<version>1.1-hudson-20090222</version>
</dependency>
<dependency>
<groupId>org.acegisecurity</groupId>
......@@ -708,6 +708,11 @@ THE SOFTWARE.
<artifactId>embedded_su4j</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.jvnet.hudson</groupId>
<artifactId>jinterop-wmi</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- offline profiler API to put in the classpath if we need it -->
<!--dependency>
......
......@@ -63,6 +63,8 @@ import org.apache.commons.jelly.JellyContext;
import org.apache.commons.jelly.JellyTagException;
import org.apache.commons.jelly.Script;
import org.apache.commons.jelly.XMLOutput;
import org.apache.commons.jelly.Tag;
import org.apache.commons.jelly.TagSupport;
import org.apache.commons.jexl.parser.ASTSizeFunction;
import org.apache.commons.jexl.util.Introspector;
import org.jvnet.animal_sniffer.IgnoreJRERequirement;
......@@ -70,6 +72,7 @@ import org.kohsuke.stapler.Ancestor;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.jelly.CustomTagLibrary.StaplerDynamicTag;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
......@@ -1040,6 +1043,21 @@ public class Functions {
return Cloud.ALL;
}
/**
* Used to assist form databinding. Given the "attrs" object,
* find the ancestor tag file of the given name.
*/
public Tag findAncestorTag(Map attributes, String nsUri, String local) {
Tag tag = (Tag) attributes.get("ownerTag");
while(tag!=null) {
tag = TagSupport.findAncestorWithClass(tag.getParent(), StaplerDynamicTag.class);
StaplerDynamicTag stag = (StaplerDynamicTag)tag;
if(stag.getLocalName().equals(local) && stag.getNsUri().equals(nsUri))
return tag;
}
return null;
}
private static final Pattern SCHEME = Pattern.compile("[a-z]+://.+");
/**
......
......@@ -114,10 +114,8 @@ public class WindowsSlaveInstaller implements Callable<Void,RuntimeException>, A
FileUtils.copyURLToFile(getClass().getResource("/windows-service/hudson.exe"), slaveExe);
// write out the descriptor
String xml = IOUtils.toString(getClass().getResourceAsStream("/windows-service/hudson-slave.xml"), "UTF-8");
xml = xml.replace("@JAVA@",System.getProperty("java.home")+"\\bin\\java.exe");
URL jnlp = new URL(engine.getHudsonUrl(),"computer/"+engine.slaveName+"/slave-agent.jnlp");
xml = xml.replace("@URL@",jnlp.toExternalForm());
String xml = generateSlaveXml(System.getProperty("java.home")+"\\bin\\java.exe", "-jnlpUrl "+jnlp.toExternalForm());
FileUtils.writeStringToFile(new File(dir, "hudson-slave.xml"),xml,"UTF-8");
// copy slave.jar
......@@ -168,5 +166,12 @@ public class WindowsSlaveInstaller implements Callable<Void,RuntimeException>, A
}
}
public static String generateSlaveXml(String java, String args) throws IOException {
String xml = IOUtils.toString(WindowsSlaveInstaller.class.getResourceAsStream("/windows-service/hudson-slave.xml"), "UTF-8");
xml = xml.replace("@JAVA@", java);
xml = xml.replace("@ARGS@", args);
return xml;
}
private static final long serialVersionUID = 1L;
}
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.os.windows;
import hudson.lifecycle.WindowsSlaveInstaller;
import hudson.model.Descriptor;
import hudson.model.Hudson;
import hudson.slaves.ComputerLauncher;
import hudson.slaves.SlaveComputer;
import hudson.util.StreamTaskListener;
import hudson.util.Secret;
import hudson.util.jna.DotNet;
import hudson.remoting.Channel;
import hudson.remoting.SocketInputStream;
import hudson.remoting.SocketOutputStream;
import hudson.remoting.Channel.Listener;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbException;
import jcifs.smb.NtlmPasswordAuthentication;
import org.apache.commons.io.IOUtils;
import org.jinterop.dcom.common.JIException;
import org.jinterop.dcom.common.JIDefaultAuthInfoImpl;
import org.jinterop.dcom.core.JISession;
import org.kohsuke.stapler.DataBoundConstructor;
import org.jvnet.hudson.wmi.WMI;
import org.jvnet.hudson.wmi.SWbemServices;
import org.jvnet.hudson.wmi.Win32Service;
import static org.jvnet.hudson.wmi.Win32Service.Win32OwnProcess;
import org.dom4j.io.SAXReader;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.PrintStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.net.UnknownHostException;
import java.net.Socket;
import java.util.logging.Logger;
import java.util.logging.Level;
/**
* Windows slave installed/managed as a service entirely remotely
*
* @author Kohsuke Kawaguchi
*/
public class ManagedWindowsServiceLauncher extends ComputerLauncher {
/**
* "[DOMAIN\\]USERNAME" to follow the Windows convention.
*/
public final String userName;
public final Secret password;
@DataBoundConstructor
public ManagedWindowsServiceLauncher(String userName, String password) {
this.userName = userName;
this.password = Secret.fromString(password);
}
private JIDefaultAuthInfoImpl createAuth() {
String[] tokens = userName.split("\\\\");
if(tokens.length==2)
return new JIDefaultAuthInfoImpl(tokens[0], tokens[1], password.toString());
return new JIDefaultAuthInfoImpl("", userName, password.toString());
}
private NtlmPasswordAuthentication createSmbAuth() {
JIDefaultAuthInfoImpl auth = createAuth();
return new NtlmPasswordAuthentication(auth.getDomain(), auth.getUserName(), auth.getPassword());
}
public void launch(final SlaveComputer computer, final StreamTaskListener listener) throws IOException, InterruptedException {
try {
PrintStream logger = listener.getLogger();
logger.println("Connecting to "+computer.getName());
JIDefaultAuthInfoImpl auth = createAuth();
JISession session = JISession.createSession(auth);
session.setGlobalSocketTimeout(60000);
SWbemServices services = WMI.connect(session, computer.getName());
String path = computer.getNode().getRemoteFS();
SmbFile remoteRoot = new SmbFile("smb://" + computer.getName() + "/" + path.replace('\\', '/').replace(':', '$')+"/",createSmbAuth());
Win32Service slaveService = services.getService("hudsonslave");
if(slaveService==null) {
logger.println("Installing the Hudson slave service");
if(!DotNet.isInstalled(2,0,computer.getName(), auth)) {
// abort the launch
logger.println(".NET Framework 2.0 or later is required on this computer to run a Hudson slave as a Windows service");
return;
}
remoteRoot.mkdirs();
// copy exe
logger.println("Copying hudson-slave.exe");
copyAndClose(getClass().getResource("/windows-service/hudson.exe").openStream(),
new SmbFile(remoteRoot,"hudson-slave.exe").getOutputStream());
copySlaveJar(logger, remoteRoot);
// copy hudson-slave.xml
logger.println("Copying hudson-slave.xml");
String xml = WindowsSlaveInstaller.generateSlaveXml("javaw.exe","-tcp %BASE%\\port.txt");
copyAndClose(new ByteArrayInputStream(xml.getBytes("UTF-8")),
new SmbFile(remoteRoot,"hudson-slave.xml").getOutputStream());
// install it as a service
logger.println("Registering the service");
Document dom = new SAXReader().read(new StringReader(xml));
Win32Service svc = services.Get("Win32_Service").cast(Win32Service.class);
int r = svc.Create(
dom.selectSingleNode("/service/id").getText(),
dom.selectSingleNode("/service/name").getText(),
path+"\\hudson-slave.exe",
Win32OwnProcess, 0, "Manual", true);
if(r!=0) {
listener.error("Failed to create a service: "+svc.getErrorMessage(r));
return;
}
slaveService = services.getService("hudsonslave");
} else {
copySlaveJar(logger, remoteRoot);
}
logger.println("Starting the service");
slaveService.start();
// wait until we see the port.txt, but don't do so forever
logger.println("Waiting for the service to become ready");
SmbFile portFile = new SmbFile(remoteRoot, "port.txt");
for( int i=0; !portFile.exists(); i++ ) {
if(i>=30) {
listener.error("The service didn't respond. Perphaps it failed to launch?");
return;
}
Thread.sleep(1000);
}
int p = readSmbFile(portFile);
// connect
logger.println("Connecting to port "+p);
final Socket s = new Socket(computer.getName(),p);
// ready
computer.setChannel(new BufferedInputStream(new SocketInputStream(s)),
new BufferedOutputStream(new SocketOutputStream(s)),
listener.getLogger(),new Listener() {
public void onClosed(Channel channel, IOException cause) {
afterDisconnect(computer,listener);
}
});
} catch (SmbException e) {
e.printStackTrace(listener.error(e.getMessage()));
} catch (JIException e) {
e.printStackTrace(listener.error(e.getMessage()));
} catch (DocumentException e) {
e.printStackTrace(listener.error(e.getMessage()));
}
}
private void copySlaveJar(PrintStream logger, SmbFile remoteRoot) throws IOException {
// copy slave.jar
logger.println("Copying slave.jar");
copyAndClose(Hudson.getInstance().getJnlpJars("slave.jar").getURL().openStream(),
new SmbFile(remoteRoot,"slave.jar").getOutputStream());
}
private int readSmbFile(SmbFile f) throws IOException {
InputStream in=null;
try {
in = f.getInputStream();
return Integer.parseInt(IOUtils.toString(in));
} finally {
IOUtils.closeQuietly(in);
}
}
@Override
public void afterDisconnect(SlaveComputer computer, StreamTaskListener listener) {
try {
JIDefaultAuthInfoImpl auth = createAuth();
JISession session = JISession.createSession(auth);
session.setGlobalSocketTimeout(60000);
SWbemServices services = WMI.connect(session, computer.getName());
Win32Service slaveService = services.getService("hudsonslave");
if(slaveService!=null) {
listener.getLogger().println("Stopping the service");
slaveService.StopService();
}
} catch (UnknownHostException e) {
e.printStackTrace(listener.error(e.getMessage()));
} catch (JIException e) {
e.printStackTrace(listener.error(e.getMessage()));
}
}
private static void copyAndClose(InputStream in, OutputStream out) {
try {
IOUtils.copy(in,out);
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(out);
}
}
public Descriptor<ComputerLauncher> getDescriptor() {
return DescriptorImpl.INSTANCE;
}
public static class DescriptorImpl extends Descriptor<ComputerLauncher> {
public static final DescriptorImpl INSTANCE = new DescriptorImpl();
public String getDisplayName() {
return "Let Hudson control this Windows slave as a Windows service";
}
}
static {
LIST.add(DescriptorImpl.INSTANCE);
Logger.getLogger("org.jinterop").setLevel(Level.WARNING);
}
}
......@@ -24,6 +24,7 @@
package hudson.slaves;
import hudson.ExtensionPoint;
import hudson.os.windows.ManagedWindowsServiceLauncher;
import hudson.model.Computer;
import hudson.model.Describable;
import hudson.remoting.Channel.Listener;
......@@ -104,5 +105,6 @@ public abstract class ComputerLauncher implements Describable<ComputerLauncher>,
static {
LIST.load(JNLPLauncher.class);
LIST.load(CommandLauncher.class);
LIST.load(ManagedWindowsServiceLauncher.class);
}
}
......@@ -231,6 +231,7 @@ public abstract class RetentionStrategy<T extends Computer> implements Describab
}
static {
LIST.load(Always.class);
LIST.load(Demand.class);
if (Boolean.getBoolean("hudson.scheduledRetention"))
LIST.load(SimpleScheduledRetentionStrategy.class);
......
......@@ -264,7 +264,7 @@ public class SlaveComputer extends Computer {
* when the established communication channel might include some data that might
* be useful for debugging/trouble-shooting.
* @param listener
* Gets a notification when the channel closes, to perform clean up.
* Gets a notification when the channel closes, to perform clean up. Can be null.
*/
public void setChannel(InputStream in, OutputStream out, OutputStream launchLog, Channel.Listener listener) throws IOException, InterruptedException {
if(this.channel!=null)
......@@ -277,7 +277,8 @@ public class SlaveComputer extends Computer {
SlaveComputer.this.channel = null;
}
});
channel.addListener(listener);
if(listener!=null)
channel.addListener(listener);
PrintWriter log = new PrintWriter(launchLog,true);
......
......@@ -23,8 +23,15 @@
*/
package hudson.util.jna;
import java.util.regex.Pattern;
import org.jinterop.dcom.common.IJIAuthInfo;
import org.jinterop.dcom.common.JIException;
import org.jinterop.winreg.IJIWinReg;
import org.jinterop.winreg.JIPolicyHandle;
import org.jinterop.winreg.JIWinRegFactory;
import java.net.UnknownHostException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* .NET related code.
......@@ -37,21 +44,14 @@ public class DotNet {
*/
public static boolean isInstalled(int major, int minor) {
try {
// see http://support.microsoft.com/?scid=kb;en-us;315291 for the basic algorithm
// see http://support.microsoft.com/?scid=kb;en-us;315291 for the basic algorithm
// observation in my registry shows that the actual key name can be things like "v2.0 SP1"
// or "v2.0.50727", so the regexp is written to accomodate this.
RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly("SOFTWARE\\Microsoft\\.NETFramework");
try {
for( String keyName : key.getSubKeys() ) {
Matcher m = VERSION_PATTERN.matcher(keyName);
if(m.matches()) {
int mj = Integer.parseInt(m.group(1));
if(mj>=major) {
int mn = Integer.parseInt(m.group(2));
if(mn>=minor)
return true;
}
}
if (matches(keyName, major, minor))
return true;
}
return false;
} finally {
......@@ -64,5 +64,49 @@ public class DotNet {
}
}
/**
* Returns true if the .NET framework of the given version (or grater) is installed
* on a remote machine.
*/
public static boolean isInstalled(int major, int minor, String targetMachine, IJIAuthInfo session) throws JIException, UnknownHostException {
IJIWinReg registry = JIWinRegFactory.getSingleTon().getWinreg(session,targetMachine,true);
JIPolicyHandle hklm=null;
JIPolicyHandle key=null;
try {
hklm = registry.winreg_OpenHKLM();
key = registry.winreg_OpenKey(hklm,"SOFTWARE\\Microsoft\\.NETFramework", IJIWinReg.KEY_READ );
for( int i=0; ; i++ ) {
String keyName = registry.winreg_EnumKey(key,i)[0];
if(matches(keyName,major,minor))
return true;
}
} catch (JIException e) {
if(e.getErrorCode()==2)
return false; // not found
throw e;
} finally {
if(hklm!=null)
registry.winreg_CloseKey(hklm);
if(key!=null)
registry.winreg_CloseKey(key);
registry.closeConnection();
}
}
private static boolean matches(String keyName, int major, int minor) {
Matcher m = VERSION_PATTERN.matcher(keyName);
if(m.matches()) {
int mj = Integer.parseInt(m.group(1));
if(mj>=major) {
int mn = Integer.parseInt(m.group(2));
if(mn>=minor)
return true;
}
}
return false;
}
private static final Pattern VERSION_PATTERN = Pattern.compile("v(\\d+)\\.(\\d+).*");
}
<!--
The MIT License
Copyright (c) 2004-2009, Sun Microsystems, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<f:entry title="${%Administrator user name}" field="userName">
<f:textbox />
</f:entry>
<f:entry title="${%Password}" field="password">
<f:password />
</f:entry>
</j:jelly>
\ No newline at end of file
<div>
Provide the name of the Windows user who has the administrative access
on this computer, such as 'Administrator'.
This information is necessary to start a process remotely.
<p>
To specify a domain user, enter values like 'DOMAIN\Administrator'.
</div>
\ No newline at end of file
......@@ -23,7 +23,7 @@ THE SOFTWARE.
-->
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:i="jelly:fmt">
<j:if test="${it.offline and !it.temporarilyOffline}">
<j:if test="${it.launchSupported and it.offline and !it.temporarilyOffline}">
<p class="error">
This node is offline because Hudson failed to launch the slave agent on it.
<a href="log">See log for more details</a>
......
......@@ -32,6 +32,11 @@ THE SOFTWARE.
<st:attribute name="title">
Name of the entry. Think of this like a label for the control.
</st:attribute>
<st:attribute name="field">
Used for the databinding. TBD. When this attribute
is specified, @help is inferred, and nested input controls don't need
the @field nor @name.
</st:attribute>
<st:attribute name="description">
If it's not obvious to the user as to what the control expects,
specify some description text (which currently gets rendered as
......@@ -51,6 +56,11 @@ THE SOFTWARE.
so it's normally something like "/plugin/foobar/help/abc.html".
</st:attribute>
</st:documentation>
<j:if test="${attrs.help==null}">
<!-- infer the help page from the current descriptor and field if possible -->
<j:set target="${attrs}" property="help"
value="${descriptor.getHelpFile(attrs.field)}" />
</j:if>
<tr>
<td class="setting-leftspace"><st:nbsp/></td>
<td class="setting-name">
......@@ -72,7 +82,7 @@ THE SOFTWARE.
${description}
</f:description>
</j:if>
<j:if test="${help!=null}">
<j:if test="${attrs.help!=null}">
<f:helpArea />
</j:if>
</j:jelly>
\ No newline at end of file
......@@ -22,10 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<!--
-->
<j:jelly xmlns:j="jelly:core" xmlns:d="jelly:define" xmlns:st="jelly:stapler">
<j:jelly xmlns:j="jelly:core" xmlns:d="jelly:define" xmlns:st="jelly:stapler" xmlns:f="/lib/form">
<st:documentation>
Glorified &lt;input type="password">
......@@ -50,7 +47,7 @@ THE SOFTWARE.
which is the recommended approach.
</st:attribute>
</st:documentation>
<j:set var="checkUrl" value="${h.getCheckUrl(attrs.checkUrl,descriptor,attrs.field)}" />
<f:prepareDatabinding />
<input class="setting-input ${h.ifThenElse(checkUrl!=null,'validated','')}"
name ="${h.defaulted(attrs.name,'_.'+attrs.field)}"
value="${h.defaulted(attrs.value,instance[attrs.field])}"
......
<!--
The MIT License
Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define">
<st:documentation>
Modifies the 'attrs.field' of the parent to inherit @field from the enclosing &lt;f:entry>
if available. Also computes the @checkUrl attribute.
</st:documentation>
<j:set var="pattrs" value="${parentScope.attrs}" />
<j:if test="${pattrs.field==null}">
<j:set target="${pattrs}" property="field"
value="${h.findAncestorTag(pattrs,'/lib/form','entry').attributes.field}" />
</j:if>
<j:set target="${pattrs}" property="checkUrl"
value="${h.getCheckUrl(pattrs.checkUrl,descriptor,pattrs.field)}" />
</j:jelly>
\ No newline at end of file
......@@ -22,7 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define">
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:f="/lib/form">
<st:documentation>
Generates an input field <tt>&lt;input type="text" ... /></tt> to be
used inside &lt;f:entry/>
......@@ -48,7 +48,7 @@ THE SOFTWARE.
which is the recommended approach.
</st:attribute>
</st:documentation>
<j:set var="checkUrl" value="${h.getCheckUrl(attrs.checkUrl,descriptor,attrs.field)}" />
<f:prepareDatabinding />
<input class="setting-input ${h.ifThenElse(checkUrl!=null,'validated','')}"
name ="${h.defaulted(attrs.name,'_.'+attrs.field)}"
value="${h.defaulted(attrs.value,instance[attrs.field])}"
......
......@@ -37,7 +37,7 @@ THE SOFTWARE.
The following value assumes that you have java in your PATH.
-->
<executable>@JAVA@</executable>
<arguments>-Xrs -jar "%BASE%\slave.jar" -jnlpUrl @URL@</arguments>
<arguments>-Xrs -jar "%BASE%\slave.jar" @ARGS@</arguments>
<!--
interactive flag causes the empty black Java window to be displayed.
I'm still debugging this.
......
......@@ -109,6 +109,14 @@ THE SOFTWARE.
</configuration>
</plugin>
<plugin><!-- we specify this in the parent POM, so this is redundant, but otherwise IntelliJ is unhappy -->
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<!--<plugin>
<groupId>org.jvnet.fix1600</groupId>
<artifactId>fix1600</artifactId>
......
......@@ -41,10 +41,16 @@ import javax.net.ssl.SSLSession;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.File;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLConnection;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
......@@ -66,6 +72,7 @@ public class Launcher {
Mode m = Mode.BINARY;
boolean ping = false;
URL slaveJnlpURL = null;
File tcpPortFile = null;
for(int i=0; i<args.length; i++) {
String arg = args[i];
......@@ -86,6 +93,14 @@ public class Launcher {
slaveJnlpURL = new URL(args[++i]);
continue;
}
if(arg.equals("-tcp")) {
if(i+1==args.length) {
System.err.println("The -tcp option is missing a file name parameter");
System.exit(1);
}
tcpPortFile = new File(args[++i]);
continue;
}
if(arg.equals("-noCertificateCheck")) {
// bypass HTTPS security check by using free-for-all trust manager
System.out.println("Skipping HTTPS certificate checks altoghether. Note that this is not secure at all.");
......@@ -108,6 +123,10 @@ public class Launcher {
if(slaveJnlpURL!=null) {
List<String> jnlpArgs = parseJnlpArguments(slaveJnlpURL);
hudson.remoting.jnlp.Main.main(jnlpArgs.toArray(new String[jnlpArgs.size()]));
} else
if(tcpPortFile!=null) {
runAsTcpServer(tcpPortFile,m,ping);
System.exit(0);
} else {
runWithStdinStdout(m, ping);
System.exit(0);
......@@ -156,6 +175,35 @@ public class Launcher {
}
}
/**
* Listens on an ephemeral port, record that port number in a port file,
* then accepts one TCP connection.
*/
private static void runAsTcpServer(File portFile, Mode m, boolean ping) throws IOException, InterruptedException {
// if no one connects for too long, assume something went wrong
// and avoid hanging foreever
ServerSocket ss = new ServerSocket(0,1);
ss.setSoTimeout(30*1000);
// write a port file to report the port number
FileWriter w = new FileWriter(portFile);
w.write(String.valueOf(ss.getLocalPort()));
w.close();
// accept just one connection and that's it.
// when we are done, remove the port file to avoid stale port file
Socket s;
try {
s = ss.accept();
ss.close();
} finally {
portFile.delete();
}
main(new BufferedInputStream(new SocketInputStream(s)),
new BufferedOutputStream(new SocketOutputStream(s)),m,ping);
}
private static void runWithStdinStdout(Mode m, boolean ping) throws IOException, InterruptedException {
// use stdin/stdout for channel communication
ttyCheck();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册