提交 b1a7477b 编写于 作者: K kohsuke

Improving convention-over-configuration for help files, and in particular I...

Improving convention-over-configuration for help files, and in particular I made it more seamless to switch between Jelly vs static HTML in help files.

I experimented with dynamically composed help page in slave ComputerLauncher, to accomodate for the extensible list. This pattern needs to be made into a tag.

Finally, picked up a new version of stapler to implement this.


git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@15354 71c3de6d-444a-0410-be80-ed276b4c234a
上级 7fdc2e84
...@@ -346,7 +346,7 @@ THE SOFTWARE. ...@@ -346,7 +346,7 @@ THE SOFTWARE.
<dependency> <dependency>
<groupId>org.kohsuke.stapler</groupId> <groupId>org.kohsuke.stapler</groupId>
<artifactId>stapler</artifactId> <artifactId>stapler</artifactId>
<version>1.93</version> <version>1.95</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.jvnet.localizer</groupId> <groupId>org.jvnet.localizer</groupId>
......
...@@ -33,13 +33,19 @@ import net.sf.json.JSONObject; ...@@ -33,13 +33,19 @@ import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.Stapler; import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.MetaClass;
import org.kohsuke.stapler.WebApp;
import org.kohsuke.stapler.jelly.JellyClassTearOff;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.jvnet.tiger_types.Types; import org.jvnet.tiger_types.Types;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.jelly.Script;
import org.apache.commons.jelly.JellyException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import javax.servlet.ServletException;
import javax.servlet.RequestDispatcher;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
...@@ -50,6 +56,7 @@ import java.util.List; ...@@ -50,6 +56,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
...@@ -59,7 +66,6 @@ import java.lang.reflect.Type; ...@@ -59,7 +66,6 @@ import java.lang.reflect.Type;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.beans.Introspector; import java.beans.Introspector;
import java.net.URL;
/** /**
* Metadata about a configurable instance. * Metadata about a configurable instance.
...@@ -355,9 +361,32 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable { ...@@ -355,9 +361,32 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable {
* null to indicate that there's no help. * null to indicate that there's no help.
*/ */
public String getHelpFile() { public String getHelpFile() {
InputStream in = getHelpStream(); return getHelpFile(null);
}
/**
* Returns the path to the help screen HTML for the given field.
*
* <p>
* The help files are assumed to be at "help/FIELDNAME.html" with possible
* locale variations.
*/
public String getHelpFile(String fieldName) {
if(fieldName==null) fieldName="";
else fieldName='/'+fieldName;
String page = "/descriptor/" + clazz.getName() + "/help"+fieldName;
try {
if(Stapler.getCurrentRequest().getView(clazz,"help"+fieldName+".jelly")!=null)
return page;
} catch (IOException e) {
throw new Error(e);
}
InputStream in = getHelpStream(fieldName);
IOUtils.closeQuietly(in); IOUtils.closeQuietly(in);
if(in!=null) return "/descriptor/"+clazz.getName()+"/help"; if(in!=null) return page;
return null; return null;
} }
...@@ -475,8 +504,17 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable { ...@@ -475,8 +504,17 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable {
/** /**
* Serves <tt>help.html</tt> from the resource of {@link #clazz}. * Serves <tt>help.html</tt> from the resource of {@link #clazz}.
*/ */
public void doHelp(StaplerResponse rsp) throws IOException { public void doHelp(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
InputStream in = getHelpStream(); String path = req.getRestOfPath();
if(path.contains("..")) throw new ServletException("Illegal path: "+path);
RequestDispatcher rd = Stapler.getCurrentRequest().getView(clazz, "help"+path+".jelly");
if(rd!=null) {// Jelly-generated help page
rd.forward(req,rsp);
return;
}
InputStream in = getHelpStream(path);
if(in==null) { if(in==null) {
rsp.sendError(SC_NOT_FOUND); rsp.sendError(SC_NOT_FOUND);
return; return;
...@@ -487,10 +525,10 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable { ...@@ -487,10 +525,10 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable {
in.close(); in.close();
} }
private InputStream getHelpStream() { private InputStream getHelpStream(String suffix) {
Locale locale = Stapler.getCurrentRequest().getLocale(); Locale locale = Stapler.getCurrentRequest().getLocale();
String base = clazz.getName().replace('.', '/') + "/help"; String base = clazz.getName().replace('.', '/') + "/help"+suffix;
InputStream in; InputStream in;
in = clazz.getClassLoader().getResourceAsStream(base + '_' + locale.getLanguage() + '_' + locale.getCountry() + '_' + locale.getVariant() + ".html"); in = clazz.getClassLoader().getResourceAsStream(base + '_' + locale.getLanguage() + '_' + locale.getCountry() + '_' + locale.getVariant() + ".html");
...@@ -516,10 +554,7 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable { ...@@ -516,10 +554,7 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable {
} }
public static <T> List<T> toList( T... values ) { public static <T> List<T> toList( T... values ) {
final ArrayList<T> r = new ArrayList<T>(); return new ArrayList<T>(Arrays.asList(values));
for (T v : values)
r.add(v);
return r;
} }
public static <T extends Describable<T>> public static <T extends Describable<T>>
...@@ -625,4 +660,4 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable { ...@@ -625,4 +660,4 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable {
throw new AssertionError(); throw new AssertionError();
} }
} }
} }
\ No newline at end of file
...@@ -27,21 +27,18 @@ import hudson.FilePath; ...@@ -27,21 +27,18 @@ import hudson.FilePath;
import hudson.Launcher; import hudson.Launcher;
import hudson.Launcher.RemoteLauncher; import hudson.Launcher.RemoteLauncher;
import hudson.Util; import hudson.Util;
import hudson.slaves.ComputerLauncher; import hudson.slaves.*;
import hudson.slaves.RetentionStrategy;
import hudson.slaves.CommandLauncher;
import hudson.slaves.JNLPLauncher;
import hudson.slaves.SlaveComputer;
import hudson.slaves.DumbSlave;
import hudson.model.Descriptor.FormException; import hudson.model.Descriptor.FormException;
import hudson.remoting.Callable; import hudson.remoting.Callable;
import hudson.remoting.VirtualChannel; import hudson.remoting.VirtualChannel;
import hudson.tasks.DynamicLabeler; import hudson.tasks.DynamicLabeler;
import hudson.tasks.LabelFinder; import hudson.tasks.LabelFinder;
import hudson.util.ClockDifference; import hudson.util.ClockDifference;
import hudson.util.FormFieldValidator.NonNegativeInteger;
import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.WebMethod;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import javax.servlet.ServletException; import javax.servlet.ServletException;
...@@ -391,6 +388,15 @@ public abstract class Slave extends Node implements Serializable { ...@@ -391,6 +388,15 @@ public abstract class Slave extends Node implements Serializable {
return this; return this;
} }
public abstract SlaveDescriptor getDescriptor();
public static abstract class SlaveDescriptor extends NodeDescriptor {
public void doCheckNumExecutors() throws IOException, ServletException {
new NonNegativeInteger().process();
}
}
// //
// backwrad compatibility // backwrad compatibility
// //
......
...@@ -25,12 +25,8 @@ package hudson.slaves; ...@@ -25,12 +25,8 @@ package hudson.slaves;
import hudson.model.Descriptor.FormException; import hudson.model.Descriptor.FormException;
import hudson.model.Slave; import hudson.model.Slave;
import hudson.util.FormFieldValidator.NonNegativeInteger;
import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundConstructor;
import javax.servlet.ServletException;
import java.io.IOException;
/** /**
* Default {@link Slave} implementation for computers that do not belong to a higher level structure, * Default {@link Slave} implementation for computers that do not belong to a higher level structure,
* like grid or cloud. * like grid or cloud.
...@@ -47,16 +43,12 @@ public final class DumbSlave extends Slave { ...@@ -47,16 +43,12 @@ public final class DumbSlave extends Slave {
return DescriptorImpl.INSTANCE; return DescriptorImpl.INSTANCE;
} }
public static final class DescriptorImpl extends NodeDescriptor { public static final class DescriptorImpl extends SlaveDescriptor {
public static final DescriptorImpl INSTANCE = new DescriptorImpl(); public static final DescriptorImpl INSTANCE = new DescriptorImpl();
public String getDisplayName() { public String getDisplayName() {
return Messages.DumbSlave_displayName(); return Messages.DumbSlave_displayName();
} }
public void doCheckNumExecutors() throws IOException, ServletException {
new NonNegativeInteger().process();
}
} }
static { static {
......
<!--
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" xmlns:i="jelly:fmt">
<l:ajax>
<div>
Controls how Hudson starts this slave.
<dl>
<j:forEach var="d" items="${h.getComputerLauncherDescriptors()}">
<dt><b>${d.displayName}</b></dt>
<dd>
<st:include class="${d.clazz}" page="help.jelly" optional="true"/>
</dd>
</j:forEach>
</dl>
</div>
</l:ajax>
</j:jelly>
\ No newline at end of file
<!--
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" xmlns:i="jelly:fmt">
Starts a slave by having Hudson execute a command from the master.
Use this when the master is capable of remotely executing a process
on a slave, such as through ssh/rsh.
</j:jelly>
\ No newline at end of file
...@@ -48,7 +48,7 @@ THE SOFTWARE. ...@@ -48,7 +48,7 @@ THE SOFTWARE.
<f:slave-mode name="mode" node="${it}" /> <f:slave-mode name="mode" node="${it}" />
<f:dropdownList name="slave.launcher" title="${%Launch method}" <f:dropdownList name="slave.launcher" title="${%Launch method}"
help="/help/system-config/master-slave/startMethod.html"> help="${descriptor.getHelpFile('launcher')}">
<j:forEach var="d" items="${h.getComputerLauncherDescriptors()}" varStatus="loop"> <j:forEach var="d" items="${h.getComputerLauncherDescriptors()}" varStatus="loop">
<f:dropdownListBlock value="${d.clazz.name}" name="${d.displayName}" <f:dropdownListBlock value="${d.clazz.name}" name="${d.displayName}"
selected="${it.launcher.descriptor==d}" selected="${it.launcher.descriptor==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" xmlns:i="jelly:fmt">
Starts a slave by launching an agent program through
<a href="http://en.wikipedia.org/wiki/Java_Web_Start">JNLP</a>.
The launch in this case is initiated by the slave,
although it is possible to start a launch without GUI, for example
as a Windows service.
</j:jelly>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册