diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java index d43be7bfedc8d762c027f5d40ffcc609f9b4930b..761e59969dd377f1275565179d820cca991b07c8 100644 --- a/core/src/main/java/hudson/ClassicPluginStrategy.java +++ b/core/src/main/java/hudson/ClassicPluginStrategy.java @@ -1,321 +1,321 @@ -package hudson; - -import hudson.PluginWrapper.Dependency; -import hudson.util.IOException2; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileReader; -import java.io.FilenameFilter; -import java.io.IOException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.List; -import java.util.jar.Manifest; -import java.util.logging.Logger; - -import org.apache.tools.ant.BuildException; -import org.apache.tools.ant.Project; -import org.apache.tools.ant.taskdefs.Expand; -import org.apache.tools.ant.types.FileSet; - -public class ClassicPluginStrategy implements PluginStrategy { - - private static final Logger LOGGER = Logger.getLogger(ClassicPluginStrategy.class.getName()); - - /** - * Filter for jar files. - */ - private static final FilenameFilter JAR_FILTER = new FilenameFilter() { - public boolean accept(File dir,String name) { - return name.endsWith(".jar"); - } - }; - - private PluginManager pluginManager; - - public ClassicPluginStrategy(PluginManager pluginManager) { - this.pluginManager = pluginManager; - } - - @Override - public PluginWrapper createPluginWrapper(File archive) throws IOException { - LOGGER.info("Loading plugin: " + archive); - - Manifest manifest; - URL baseResourceURL; - - boolean isLinked = archive.getName().endsWith(".hpl"); - - File expandDir = null; - // if .hpi, this is the directory where war is expanded - - if (isLinked) { - // resolve the .hpl file to the location of the manifest file - String firstLine = new BufferedReader(new FileReader(archive)) - .readLine(); - if (firstLine.startsWith("Manifest-Version:")) { - // this is the manifest already - } else { - // indirection - archive = resolve(archive, firstLine); - } - // then parse manifest - FileInputStream in = new FileInputStream(archive); - try { - manifest = new Manifest(in); - } catch (IOException e) { - throw new IOException2("Failed to load " + archive, e); - } finally { - in.close(); - } - } else { - expandDir = new File(archive.getParentFile(), PluginWrapper.getBaseName(archive)); - explode(archive, expandDir); - - File manifestFile = new File(expandDir, "META-INF/MANIFEST.MF"); - if (!manifestFile.exists()) { - throw new IOException( - "Plugin installation failed. No manifest at " - + manifestFile); - } - FileInputStream fin = new FileInputStream(manifestFile); - try { - manifest = new Manifest(fin); - } finally { - fin.close(); - } - } - - // TODO: define a mechanism to hide classes - // String export = manifest.getMainAttributes().getValue("Export"); - - List paths = new ArrayList(); - if (isLinked) { - parseClassPath(manifest, archive, paths, "Libraries", ","); - parseClassPath(manifest, archive, paths, "Class-Path", " +"); // backward - // compatibility - - baseResourceURL = resolve(archive, - manifest.getMainAttributes().getValue("Resource-Path")) - .toURL(); - } else { - File classes = new File(expandDir, "WEB-INF/classes"); - if (classes.exists()) - paths.add(classes.toURL()); - File lib = new File(expandDir, "WEB-INF/lib"); - File[] libs = lib.listFiles(JAR_FILTER); - if (libs != null) { - for (File jar : libs) - paths.add(jar.toURL()); - } - - baseResourceURL = expandDir.toURL(); - } - File disableFile = new File(archive.getPath() + ".disabled"); - if (disableFile.exists()) { - LOGGER.info("Plugin is disabled"); - } - - // compute dependencies - List dependencies = new ArrayList(); - List optionalDependencies = new ArrayList(); - String v = manifest.getMainAttributes().getValue("Plugin-Dependencies"); - if (v != null) { - for (String s : v.split(",")) { - PluginWrapper.Dependency d = new PluginWrapper.Dependency(s); - if (d.optional) { - optionalDependencies.add(d); - } else { - dependencies.add(d); - } - } - } - - ClassLoader dependencyLoader = new DependencyClassLoader(getClass() - .getClassLoader(), dependencies); - ClassLoader classLoader = new URLClassLoader(paths.toArray(new URL[paths.size()]), - dependencyLoader); - - return new PluginWrapper(archive, manifest, baseResourceURL, - classLoader, disableFile, dependencies, optionalDependencies); - } - - @Override - public void initializeComponents(PluginWrapper plugin) { - } - - @Override - public void load(PluginWrapper wrapper) throws IOException { - String className = wrapper.getPluginClass(); - if(className ==null) { - throw new IOException("Plugin installation failed. No 'Plugin-Class' entry in the manifest of "+wrapper.getShortName()); - } - - loadPluginDependencies(wrapper.getDependencies(), - wrapper.getOptionalDependencies()); - - if (!wrapper.isActive()) - return; - - // override the context classloader so that XStream activity in plugin.start() - // will be able to resolve classes in this plugin - ClassLoader old = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(wrapper.classLoader); - try { - try { - Class clazz = wrapper.classLoader.loadClass(className); - Object o = clazz.newInstance(); - if(!(o instanceof Plugin)) { - throw new IOException(className+" doesn't extend from hudson.Plugin"); - } - wrapper.setPlugin((Plugin) o); - } catch (ClassNotFoundException e) { - throw new IOException2("Unable to load " + className + " from " + wrapper.getShortName(),e); - } catch (IllegalAccessException e) { - throw new IOException2("Unable to create instance of " + className + " from " + wrapper.getShortName(),e); - } catch (InstantiationException e) { - throw new IOException2("Unable to create instance of " + className + " from " + wrapper.getShortName(),e); - } - - // initialize plugin - try { - Plugin plugin = wrapper.getPlugin(); - plugin.setServletContext(pluginManager.context); - startPlugin(wrapper); - } catch(Throwable t) { - // gracefully handle any error in plugin. - throw new IOException2("Failed to initialize",t); - } - } finally { - Thread.currentThread().setContextClassLoader(old); - } - } - - public void startPlugin(PluginWrapper plugin) throws Exception { - plugin.getPlugin().start(); - } - - private static File resolve(File base, String relative) { - File rel = new File(relative); - if(rel.isAbsolute()) - return rel; - else - return new File(base.getParentFile(),relative); - } - - private static void parseClassPath(Manifest manifest, File archive, List paths, String attributeName, String separator) throws IOException { - String classPath = manifest.getMainAttributes().getValue(attributeName); - if(classPath==null) return; // attribute not found - for (String s : classPath.split(separator)) { - File file = resolve(archive, s); - if(file.getName().contains("*")) { - // handle wildcard - FileSet fs = new FileSet(); - File dir = file.getParentFile(); - fs.setDir(dir); - fs.setIncludes(file.getName()); - for( String included : fs.getDirectoryScanner(new Project()).getIncludedFiles() ) { - paths.add(new File(dir,included).toURL()); - } - } else { - if(!file.exists()) - throw new IOException("No such file: "+file); - paths.add(file.toURL()); - } - } - } - - /** - * Explodes the plugin into a directory, if necessary. - */ - private static void explode(File archive, File destDir) throws IOException { - if(!destDir.exists()) - destDir.mkdirs(); - - // timestamp check - File explodeTime = new File(destDir,".timestamp"); - if(explodeTime.exists() && explodeTime.lastModified()>archive.lastModified()) - return; // no need to expand - - LOGGER.info("Extracting "+archive); - - // delete the contents so that old files won't interfere with new files - Util.deleteContentsRecursive(destDir); - - try { - Expand e = new Expand(); - e.setProject(new Project()); - e.setTaskType("unzip"); - e.setSrc(archive); - e.setDest(destDir); - e.execute(); - } catch (BuildException x) { - IOException ioe = new IOException("Failed to expand " + archive); - ioe.initCause(x); - throw ioe; - } - - Util.touch(explodeTime); - } - - /** - * Loads the dependencies to other plugins. - * - * @throws IOException - * thrown if one or several mandatory dependencies doesnt - * exists. - */ - private void loadPluginDependencies(List dependencies, - List optionalDependencies) throws IOException { - List missingDependencies = new ArrayList(); - // make sure dependencies exist - for (Dependency d : dependencies) { - if (pluginManager.getPlugin(d.shortName) == null) - missingDependencies.add(d.toString()); - } - if (!missingDependencies.isEmpty()) { - StringBuilder builder = new StringBuilder(); - builder.append("Dependency "); - builder.append(Util.join(missingDependencies, ", ")); - builder.append(" doesn't exist"); - throw new IOException(builder.toString()); - } - - // add the optional dependencies that exists - for (Dependency d : optionalDependencies) { - if (pluginManager.getPlugin(d.shortName) != null) - dependencies.add(d); - } - } - - /** - * Used to load classes from dependency plugins. - */ - final class DependencyClassLoader extends ClassLoader { - private List dependencies; - - public DependencyClassLoader(ClassLoader parent, List dependencies) { - super(parent); - this.dependencies = dependencies; - } - - protected Class findClass(String name) throws ClassNotFoundException { - for (Dependency dep : dependencies) { - PluginWrapper p = pluginManager.getPlugin(dep.shortName); - if(p!=null) - try { - return p.classLoader.loadClass(name); - } catch (ClassNotFoundException _) { - // try next - } - } - - throw new ClassNotFoundException(name); - } - - // TODO: delegate resources? watch out for diamond dependencies - } -} +package hudson; + +import hudson.PluginWrapper.Dependency; +import hudson.util.IOException2; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.FilenameFilter; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.Manifest; +import java.util.logging.Logger; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Expand; +import org.apache.tools.ant.types.FileSet; + +public class ClassicPluginStrategy implements PluginStrategy { + + private static final Logger LOGGER = Logger.getLogger(ClassicPluginStrategy.class.getName()); + + /** + * Filter for jar files. + */ + private static final FilenameFilter JAR_FILTER = new FilenameFilter() { + public boolean accept(File dir,String name) { + return name.endsWith(".jar"); + } + }; + + private PluginManager pluginManager; + + public ClassicPluginStrategy(PluginManager pluginManager) { + this.pluginManager = pluginManager; + } + + @Override + public PluginWrapper createPluginWrapper(File archive) throws IOException { + LOGGER.info("Loading plugin: " + archive); + + Manifest manifest; + URL baseResourceURL; + + boolean isLinked = archive.getName().endsWith(".hpl"); + + File expandDir = null; + // if .hpi, this is the directory where war is expanded + + if (isLinked) { + // resolve the .hpl file to the location of the manifest file + String firstLine = new BufferedReader(new FileReader(archive)) + .readLine(); + if (firstLine.startsWith("Manifest-Version:")) { + // this is the manifest already + } else { + // indirection + archive = resolve(archive, firstLine); + } + // then parse manifest + FileInputStream in = new FileInputStream(archive); + try { + manifest = new Manifest(in); + } catch (IOException e) { + throw new IOException2("Failed to load " + archive, e); + } finally { + in.close(); + } + } else { + expandDir = new File(archive.getParentFile(), PluginWrapper.getBaseName(archive)); + explode(archive, expandDir); + + File manifestFile = new File(expandDir, "META-INF/MANIFEST.MF"); + if (!manifestFile.exists()) { + throw new IOException( + "Plugin installation failed. No manifest at " + + manifestFile); + } + FileInputStream fin = new FileInputStream(manifestFile); + try { + manifest = new Manifest(fin); + } finally { + fin.close(); + } + } + + // TODO: define a mechanism to hide classes + // String export = manifest.getMainAttributes().getValue("Export"); + + List paths = new ArrayList(); + if (isLinked) { + parseClassPath(manifest, archive, paths, "Libraries", ","); + parseClassPath(manifest, archive, paths, "Class-Path", " +"); // backward + // compatibility + + baseResourceURL = resolve(archive, + manifest.getMainAttributes().getValue("Resource-Path")) + .toURL(); + } else { + File classes = new File(expandDir, "WEB-INF/classes"); + if (classes.exists()) + paths.add(classes.toURL()); + File lib = new File(expandDir, "WEB-INF/lib"); + File[] libs = lib.listFiles(JAR_FILTER); + if (libs != null) { + for (File jar : libs) + paths.add(jar.toURL()); + } + + baseResourceURL = expandDir.toURL(); + } + File disableFile = new File(archive.getPath() + ".disabled"); + if (disableFile.exists()) { + LOGGER.info("Plugin is disabled"); + } + + // compute dependencies + List dependencies = new ArrayList(); + List optionalDependencies = new ArrayList(); + String v = manifest.getMainAttributes().getValue("Plugin-Dependencies"); + if (v != null) { + for (String s : v.split(",")) { + PluginWrapper.Dependency d = new PluginWrapper.Dependency(s); + if (d.optional) { + optionalDependencies.add(d); + } else { + dependencies.add(d); + } + } + } + + ClassLoader dependencyLoader = new DependencyClassLoader(getClass() + .getClassLoader(), dependencies); + ClassLoader classLoader = new URLClassLoader(paths.toArray(new URL[paths.size()]), + dependencyLoader); + + return new PluginWrapper(archive, manifest, baseResourceURL, + classLoader, disableFile, dependencies, optionalDependencies); + } + + @Override + public void initializeComponents(PluginWrapper plugin) { + } + + @Override + public void load(PluginWrapper wrapper) throws IOException { + String className = wrapper.getPluginClass(); + if(className ==null) { + throw new IOException("Plugin installation failed. No 'Plugin-Class' entry in the manifest of "+wrapper.getShortName()); + } + + loadPluginDependencies(wrapper.getDependencies(), + wrapper.getOptionalDependencies()); + + if (!wrapper.isActive()) + return; + + // override the context classloader so that XStream activity in plugin.start() + // will be able to resolve classes in this plugin + ClassLoader old = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(wrapper.classLoader); + try { + try { + Class clazz = wrapper.classLoader.loadClass(className); + Object o = clazz.newInstance(); + if(!(o instanceof Plugin)) { + throw new IOException(className+" doesn't extend from hudson.Plugin"); + } + wrapper.setPlugin((Plugin) o); + } catch (ClassNotFoundException e) { + throw new IOException2("Unable to load " + className + " from " + wrapper.getShortName(),e); + } catch (IllegalAccessException e) { + throw new IOException2("Unable to create instance of " + className + " from " + wrapper.getShortName(),e); + } catch (InstantiationException e) { + throw new IOException2("Unable to create instance of " + className + " from " + wrapper.getShortName(),e); + } + + // initialize plugin + try { + Plugin plugin = wrapper.getPlugin(); + plugin.setServletContext(pluginManager.context); + startPlugin(wrapper); + } catch(Throwable t) { + // gracefully handle any error in plugin. + throw new IOException2("Failed to initialize",t); + } + } finally { + Thread.currentThread().setContextClassLoader(old); + } + } + + public void startPlugin(PluginWrapper plugin) throws Exception { + plugin.getPlugin().start(); + } + + private static File resolve(File base, String relative) { + File rel = new File(relative); + if(rel.isAbsolute()) + return rel; + else + return new File(base.getParentFile(),relative); + } + + private static void parseClassPath(Manifest manifest, File archive, List paths, String attributeName, String separator) throws IOException { + String classPath = manifest.getMainAttributes().getValue(attributeName); + if(classPath==null) return; // attribute not found + for (String s : classPath.split(separator)) { + File file = resolve(archive, s); + if(file.getName().contains("*")) { + // handle wildcard + FileSet fs = new FileSet(); + File dir = file.getParentFile(); + fs.setDir(dir); + fs.setIncludes(file.getName()); + for( String included : fs.getDirectoryScanner(new Project()).getIncludedFiles() ) { + paths.add(new File(dir,included).toURL()); + } + } else { + if(!file.exists()) + throw new IOException("No such file: "+file); + paths.add(file.toURL()); + } + } + } + + /** + * Explodes the plugin into a directory, if necessary. + */ + private static void explode(File archive, File destDir) throws IOException { + if(!destDir.exists()) + destDir.mkdirs(); + + // timestamp check + File explodeTime = new File(destDir,".timestamp"); + if(explodeTime.exists() && explodeTime.lastModified()>archive.lastModified()) + return; // no need to expand + + LOGGER.info("Extracting "+archive); + + // delete the contents so that old files won't interfere with new files + Util.deleteContentsRecursive(destDir); + + try { + Expand e = new Expand(); + e.setProject(new Project()); + e.setTaskType("unzip"); + e.setSrc(archive); + e.setDest(destDir); + e.execute(); + } catch (BuildException x) { + IOException ioe = new IOException("Failed to expand " + archive); + ioe.initCause(x); + throw ioe; + } + + Util.touch(explodeTime); + } + + /** + * Loads the dependencies to other plugins. + * + * @throws IOException + * thrown if one or several mandatory dependencies doesnt + * exists. + */ + private void loadPluginDependencies(List dependencies, + List optionalDependencies) throws IOException { + List missingDependencies = new ArrayList(); + // make sure dependencies exist + for (Dependency d : dependencies) { + if (pluginManager.getPlugin(d.shortName) == null) + missingDependencies.add(d.toString()); + } + if (!missingDependencies.isEmpty()) { + StringBuilder builder = new StringBuilder(); + builder.append("Dependency "); + builder.append(Util.join(missingDependencies, ", ")); + builder.append(" doesn't exist"); + throw new IOException(builder.toString()); + } + + // add the optional dependencies that exists + for (Dependency d : optionalDependencies) { + if (pluginManager.getPlugin(d.shortName) != null) + dependencies.add(d); + } + } + + /** + * Used to load classes from dependency plugins. + */ + final class DependencyClassLoader extends ClassLoader { + private List dependencies; + + public DependencyClassLoader(ClassLoader parent, List dependencies) { + super(parent); + this.dependencies = dependencies; + } + + protected Class findClass(String name) throws ClassNotFoundException { + for (Dependency dep : dependencies) { + PluginWrapper p = pluginManager.getPlugin(dep.shortName); + if(p!=null) + try { + return p.classLoader.loadClass(name); + } catch (ClassNotFoundException _) { + // try next + } + } + + throw new ClassNotFoundException(name); + } + + // TODO: delegate resources? watch out for diamond dependencies + } +} diff --git a/core/src/main/java/hudson/PluginStrategy.java b/core/src/main/java/hudson/PluginStrategy.java index 409f680c5f35a93e4ec881ea7f35e4cef59abd0b..d11053fcf6d67fd1239a2a416be593a7fce86c4d 100644 --- a/core/src/main/java/hudson/PluginStrategy.java +++ b/core/src/main/java/hudson/PluginStrategy.java @@ -1,44 +1,44 @@ -package hudson; - -import java.io.File; -import java.io.IOException; - -/** - * Pluggability point for how to create {@link PluginWrapper}. - * - *

- * This extension point was added to allow plugins to be loaded into a different environment - * (such as loading it in an existing DI container like Plexus.) A plugin strategy is a singleton - * instance, and as such this feature is primarily meant for OEM. - * - * See {@link PluginManager#createPluginStrategy()} for how this instance is created. - */ -public interface PluginStrategy extends ExtensionPoint { - - /** - * Creates a plugin wrapper, which provides a management interface for the plugin - * @param archive - * @return - * @throws IOException - */ - public abstract PluginWrapper createPluginWrapper(File archive) - throws IOException; - - /** - * Loads the plugin and starts it. - * - *

- * This should be done after all the classloaders are constructed for all - * the plugins, so that dependencies can be properly loaded by plugins. - */ - public abstract void load(PluginWrapper wrapper) throws IOException; - - /** - * Optionally start services provided by the plugin. Should be called - * when all plugins are loaded. - * - * @param plugin - */ - public abstract void initializeComponents(PluginWrapper plugin); - +package hudson; + +import java.io.File; +import java.io.IOException; + +/** + * Pluggability point for how to create {@link PluginWrapper}. + * + *

+ * This extension point was added to allow plugins to be loaded into a different environment + * (such as loading it in an existing DI container like Plexus.) A plugin strategy is a singleton + * instance, and as such this feature is primarily meant for OEM. + * + * See {@link PluginManager#createPluginStrategy()} for how this instance is created. + */ +public interface PluginStrategy extends ExtensionPoint { + + /** + * Creates a plugin wrapper, which provides a management interface for the plugin + * @param archive + * @return + * @throws IOException + */ + public abstract PluginWrapper createPluginWrapper(File archive) + throws IOException; + + /** + * Loads the plugin and starts it. + * + *

+ * This should be done after all the classloaders are constructed for all + * the plugins, so that dependencies can be properly loaded by plugins. + */ + public abstract void load(PluginWrapper wrapper) throws IOException; + + /** + * Optionally start services provided by the plugin. Should be called + * when all plugins are loaded. + * + * @param plugin + */ + public abstract void initializeComponents(PluginWrapper plugin); + } \ No newline at end of file diff --git a/core/src/main/java/hudson/model/ExecutorListener.java b/core/src/main/java/hudson/model/ExecutorListener.java index e7bd5969b1799d53397d2054c1eed84fdc848eba..3432b983b6d09ada742de3d59adc862ca6db6dcc 100644 --- a/core/src/main/java/hudson/model/ExecutorListener.java +++ b/core/src/main/java/hudson/model/ExecutorListener.java @@ -1,34 +1,34 @@ -package hudson.model; - -/** - * A listener for task related events from Executors -* -* @author Stephen Connolly -* @since 17-Jun-2008 18:58:12 -*/ -public interface ExecutorListener { - - /** - * Called whenever a task is accepted by an executor. - * @param executor The executor. - * @param task The task. - */ - void taskAccepted(Executor executor, Queue.Task task); - - /** - * Called whenever a task is completed without any problems by an executor. - * @param executor The executor. - * @param task The task. - * @param durationMS The number of milliseconds that the task took to complete. - */ - void taskCompleted(Executor executor, Queue.Task task, long durationMS); - - /** - * Called whenever a task is completed without any problems by an executor. - * @param executor The executor. - * @param task The task. - * @param durationMS The number of milliseconds that the task took to complete. - * @param problems The exception that was thrown. - */ - void taskCompletedWithProblems(Executor executor, Queue.Task task, long durationMS, Throwable problems); -} +package hudson.model; + +/** + * A listener for task related events from Executors +* +* @author Stephen Connolly +* @since 17-Jun-2008 18:58:12 +*/ +public interface ExecutorListener { + + /** + * Called whenever a task is accepted by an executor. + * @param executor The executor. + * @param task The task. + */ + void taskAccepted(Executor executor, Queue.Task task); + + /** + * Called whenever a task is completed without any problems by an executor. + * @param executor The executor. + * @param task The task. + * @param durationMS The number of milliseconds that the task took to complete. + */ + void taskCompleted(Executor executor, Queue.Task task, long durationMS); + + /** + * Called whenever a task is completed without any problems by an executor. + * @param executor The executor. + * @param task The task. + * @param durationMS The number of milliseconds that the task took to complete. + * @param problems The exception that was thrown. + */ + void taskCompletedWithProblems(Executor executor, Queue.Task task, long durationMS, Throwable problems); +} diff --git a/core/src/main/java/hudson/model/JobParameterDefinition.java b/core/src/main/java/hudson/model/JobParameterDefinition.java index 42dea3c2abaae0b92e8fc794c036920fa4961a4b..3dac85fc168f040a4bbb2d16fc7fb87ce1fc518a 100644 --- a/core/src/main/java/hudson/model/JobParameterDefinition.java +++ b/core/src/main/java/hudson/model/JobParameterDefinition.java @@ -1,45 +1,45 @@ -package hudson.model; - -import net.sf.json.JSONObject; - -import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.StaplerRequest; - -public class JobParameterDefinition extends ParameterDefinition { - - @DataBoundConstructor - public JobParameterDefinition(String name) { - super(name); - } - - @Override - public ParameterDescriptor getDescriptor() { - return DESCRIPTOR; - } - - public static final ParameterDescriptor DESCRIPTOR = new DescriptorImpl(); - - public static class DescriptorImpl extends ParameterDescriptor { - - protected DescriptorImpl() { - super(JobParameterDefinition.class); - } - - @Override - public String getDisplayName() { - return "Project Parameter"; - } - - @Override - public ParameterDefinition newInstance(StaplerRequest req, JSONObject formData) throws FormException { - return req.bindJSON(JobParameterDefinition.class, formData); - } - - } - - @Override - public ParameterValue createValue(StaplerRequest req, JSONObject jo) { - return req.bindJSON(JobParameterValue.class, jo); - } - -} +package hudson.model; + +import net.sf.json.JSONObject; + +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.StaplerRequest; + +public class JobParameterDefinition extends ParameterDefinition { + + @DataBoundConstructor + public JobParameterDefinition(String name) { + super(name); + } + + @Override + public ParameterDescriptor getDescriptor() { + return DESCRIPTOR; + } + + public static final ParameterDescriptor DESCRIPTOR = new DescriptorImpl(); + + public static class DescriptorImpl extends ParameterDescriptor { + + protected DescriptorImpl() { + super(JobParameterDefinition.class); + } + + @Override + public String getDisplayName() { + return "Project Parameter"; + } + + @Override + public ParameterDefinition newInstance(StaplerRequest req, JSONObject formData) throws FormException { + return req.bindJSON(JobParameterDefinition.class, formData); + } + + } + + @Override + public ParameterValue createValue(StaplerRequest req, JSONObject jo) { + return req.bindJSON(JobParameterValue.class, jo); + } + +} diff --git a/core/src/main/java/hudson/model/JobParameterValue.java b/core/src/main/java/hudson/model/JobParameterValue.java index 32e3b09e69710c7feef7d23899da28aad1a8a754..ab27ba4990c38adbffa3a9654c6dbb393dda2830 100644 --- a/core/src/main/java/hudson/model/JobParameterValue.java +++ b/core/src/main/java/hudson/model/JobParameterValue.java @@ -1,24 +1,24 @@ -package hudson.model; - -import org.kohsuke.stapler.DataBoundConstructor; - -import java.util.Map; - -public class JobParameterValue extends ParameterValue { - public final Job job; - - @DataBoundConstructor - public JobParameterValue(String name, Job job) { - super(name); - this.job = job; - } - - /** - * Exposes the name/value as an environment variable. - */ - @Override - public void buildEnvVars(AbstractBuild build, Map env) { - // TODO: check with Tom if this is really what he had in mind - env.put(name.toUpperCase(),job.toString()); - } -} +package hudson.model; + +import org.kohsuke.stapler.DataBoundConstructor; + +import java.util.Map; + +public class JobParameterValue extends ParameterValue { + public final Job job; + + @DataBoundConstructor + public JobParameterValue(String name, Job job) { + super(name); + this.job = job; + } + + /** + * Exposes the name/value as an environment variable. + */ + @Override + public void buildEnvVars(AbstractBuild build, Map env) { + // TODO: check with Tom if this is really what he had in mind + env.put(name.toUpperCase(),job.toString()); + } +} diff --git a/core/src/main/java/hudson/model/MyView.java b/core/src/main/java/hudson/model/MyView.java index 11957016304d6e3d50efb3673d94364920f13b6b..3aafb3627e6871dc75e71ee7546c7e30aefc5a14 100644 --- a/core/src/main/java/hudson/model/MyView.java +++ b/core/src/main/java/hudson/model/MyView.java @@ -1,99 +1,99 @@ -package hudson.model; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import javax.servlet.ServletException; - -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; - -/** - * {@link View} that only contains projects for which the current user has access to. - * - * @since 1.220 - * @author Tom Huybrechts - */ -public class MyView extends View { - - private final Hudson owner; - private String description; - - public MyView(Hudson owner) { - this.owner = owner; - } - - @Override - public boolean contains(TopLevelItem item) { - return (item instanceof Job) && ((Job) item).hasPermission(Hudson.ADMINISTER); - } - - @Override - public Item doCreateItem(StaplerRequest req, StaplerResponse rsp) - throws IOException, ServletException { - return owner.doCreateItem(req, rsp); - } - - @Override - public String getDescription() { - return description; - } - - /** - * Returns the transient {@link Action}s associated with the top page. - * - * @see Hudson#getActions() - */ - public List getActions() { - return Hudson.getInstance().getActions(); - } - - @Override - public TopLevelItem getItem(String name) { - return owner.getItem(name); - } - - @Override - public Collection getItems() { - List items = new ArrayList(); - for (TopLevelItem item : owner.getItems()) { - if ((item instanceof Job) && ((Job) item).hasPermission(Job.CONFIGURE)) { - items.add(item); - } - } - return Collections.unmodifiableList(items); - } - - @Override - public String getUrl() { - return "view/" + getViewName() + "/"; - } - - @Override - public String getViewName() { - return getDisplayName(); - } - - public String getDisplayName() { - return "My Projects"; - } - - public TopLevelItem getJob(String name) { - return getItem(name); - } - - /** - * Accepts the new description. - */ - public synchronized void doSubmitDescription(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - checkPermission(CONFIGURE); - - req.setCharacterEncoding("UTF-8"); - description = req.getParameter("description"); - owner.save(); - rsp.sendRedirect("."); // go to the top page - } -} +package hudson.model; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import javax.servlet.ServletException; + +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; + +/** + * {@link View} that only contains projects for which the current user has access to. + * + * @since 1.220 + * @author Tom Huybrechts + */ +public class MyView extends View { + + private final Hudson owner; + private String description; + + public MyView(Hudson owner) { + this.owner = owner; + } + + @Override + public boolean contains(TopLevelItem item) { + return (item instanceof Job) && ((Job) item).hasPermission(Hudson.ADMINISTER); + } + + @Override + public Item doCreateItem(StaplerRequest req, StaplerResponse rsp) + throws IOException, ServletException { + return owner.doCreateItem(req, rsp); + } + + @Override + public String getDescription() { + return description; + } + + /** + * Returns the transient {@link Action}s associated with the top page. + * + * @see Hudson#getActions() + */ + public List getActions() { + return Hudson.getInstance().getActions(); + } + + @Override + public TopLevelItem getItem(String name) { + return owner.getItem(name); + } + + @Override + public Collection getItems() { + List items = new ArrayList(); + for (TopLevelItem item : owner.getItems()) { + if ((item instanceof Job) && ((Job) item).hasPermission(Job.CONFIGURE)) { + items.add(item); + } + } + return Collections.unmodifiableList(items); + } + + @Override + public String getUrl() { + return "view/" + getViewName() + "/"; + } + + @Override + public String getViewName() { + return getDisplayName(); + } + + public String getDisplayName() { + return "My Projects"; + } + + public TopLevelItem getJob(String name) { + return getItem(name); + } + + /** + * Accepts the new description. + */ + public synchronized void doSubmitDescription(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { + checkPermission(CONFIGURE); + + req.setCharacterEncoding("UTF-8"); + description = req.getParameter("description"); + owner.save(); + rsp.sendRedirect("."); // go to the top page + } +} diff --git a/core/src/main/java/hudson/model/ParameterDefinition.java b/core/src/main/java/hudson/model/ParameterDefinition.java index d0db9b39e143fb422368b76121434e7f67d664b1..689cff7396c1d52e201dc0ba79881e51e0ae3cab 100644 --- a/core/src/main/java/hudson/model/ParameterDefinition.java +++ b/core/src/main/java/hudson/model/ParameterDefinition.java @@ -1,118 +1,118 @@ -package hudson.model; - -import net.sf.json.JSONObject; - -import org.kohsuke.stapler.StaplerRequest; - -import hudson.util.DescriptorList; -import hudson.ExtensionPoint; - -/** - * Defines a parameter for a build. - * - *

- * In Hudson, a user can configure a job to require parameters for a build. - * For example, imagine a test job that takes the bits to be tested as a parameter. - * - *

- * The actual meaning and the purpose of parameters are entirely up to users, so - * what the concrete parameter implmentation is pluggable. Write subclasses - * in a plugin and hook it up to {@link #LIST} to register it. - * - *

- * Three classes are used to model build parameters. First is the - * {@link ParameterDescriptor}, which tells Hudson what kind of implementations are - * available. From {@link ParameterDescriptor#newInstance(StaplerRequest, JSONObject)}, - * Hudson creates {@link ParameterDefinition}s based on the job configuration. - * For example, if the user defines two string parameters "database-type" and - * "appserver-type", we'll get two {@link StringParameterDefinition} instances - * with their respective names. - * - *

- * When a job is configured with {@link ParameterDefinition} (or more precisely, - * {@link ParametersDefinitionProperty}, which in turns retains {@link ParameterDefinition}s), - * user would have to enter the values for the defined build parameters. - * The {@link #createValue(StaplerRequest, JSONObject)} method is used to convert this - * form submission into {@link ParameterValue} objects, which are then accessible - * during a build. - * - * - * - *

Persistence

- *

- * Instances of {@link ParameterDefinition}s are persisted into job config.xml - * through XStream. - * - * - *

Assocaited Views

- *

config.jelly

- *

- * {@link ParameterDefinition} class uses config.jelly to provide contribute a form - * fragment in the job configuration screen. Values entered there is fed back to - * {@link ParameterDescriptor#newInstance(StaplerRequest, JSONObject)} to create {@link ParameterDefinition}s. - * - *

index.jelly

- * The index.jelly view contributes a form fragment in the page where the user - * enters actual values of parameters for a build. The result of this form submission - * is then fed to {@link ParameterDefinition#createValue(StaplerRequest, JSONObject)} to - * create {@link ParameterValue}s. - * - * TODO: what Jelly pages does this object need for rendering UI? - * TODO: {@link ParameterValue} needs to have some mechanism to expose values to the build - * @see StringParameterDefinition - */ -public abstract class ParameterDefinition implements - Describable, ExtensionPoint { - - private final String name; - - public String getName() { - return name; - } - - public ParameterDefinition(String name) { - super(); - this.name = name; - } - - /** - * {@inheritDoc} - */ - public abstract ParameterDescriptor getDescriptor(); - - public abstract ParameterValue createValue(StaplerRequest req, JSONObject jo); - - /** - * Returns default parameter value for this definition. - * - * @return default parameter value or null if no defaults are available - * @since 1.253 - */ - public ParameterValue getDefaultParameterValue() { - return null; - } - - /** - * A list of available parameter definition types - */ - public static final DescriptorList LIST = new DescriptorList(); - - public abstract static class ParameterDescriptor extends - Descriptor { - - protected ParameterDescriptor(Class klazz) { - super(klazz); - } - - public String getValuePage() { - return getViewPage(clazz, "index.jelly"); - } - - @Override - public String getDisplayName() { - return "Parameter"; - } - - } - -} +package hudson.model; + +import net.sf.json.JSONObject; + +import org.kohsuke.stapler.StaplerRequest; + +import hudson.util.DescriptorList; +import hudson.ExtensionPoint; + +/** + * Defines a parameter for a build. + * + *

+ * In Hudson, a user can configure a job to require parameters for a build. + * For example, imagine a test job that takes the bits to be tested as a parameter. + * + *

+ * The actual meaning and the purpose of parameters are entirely up to users, so + * what the concrete parameter implmentation is pluggable. Write subclasses + * in a plugin and hook it up to {@link #LIST} to register it. + * + *

+ * Three classes are used to model build parameters. First is the + * {@link ParameterDescriptor}, which tells Hudson what kind of implementations are + * available. From {@link ParameterDescriptor#newInstance(StaplerRequest, JSONObject)}, + * Hudson creates {@link ParameterDefinition}s based on the job configuration. + * For example, if the user defines two string parameters "database-type" and + * "appserver-type", we'll get two {@link StringParameterDefinition} instances + * with their respective names. + * + *

+ * When a job is configured with {@link ParameterDefinition} (or more precisely, + * {@link ParametersDefinitionProperty}, which in turns retains {@link ParameterDefinition}s), + * user would have to enter the values for the defined build parameters. + * The {@link #createValue(StaplerRequest, JSONObject)} method is used to convert this + * form submission into {@link ParameterValue} objects, which are then accessible + * during a build. + * + * + * + *

Persistence

+ *

+ * Instances of {@link ParameterDefinition}s are persisted into job config.xml + * through XStream. + * + * + *

Assocaited Views

+ *

config.jelly

+ *

+ * {@link ParameterDefinition} class uses config.jelly to provide contribute a form + * fragment in the job configuration screen. Values entered there is fed back to + * {@link ParameterDescriptor#newInstance(StaplerRequest, JSONObject)} to create {@link ParameterDefinition}s. + * + *

index.jelly

+ * The index.jelly view contributes a form fragment in the page where the user + * enters actual values of parameters for a build. The result of this form submission + * is then fed to {@link ParameterDefinition#createValue(StaplerRequest, JSONObject)} to + * create {@link ParameterValue}s. + * + * TODO: what Jelly pages does this object need for rendering UI? + * TODO: {@link ParameterValue} needs to have some mechanism to expose values to the build + * @see StringParameterDefinition + */ +public abstract class ParameterDefinition implements + Describable, ExtensionPoint { + + private final String name; + + public String getName() { + return name; + } + + public ParameterDefinition(String name) { + super(); + this.name = name; + } + + /** + * {@inheritDoc} + */ + public abstract ParameterDescriptor getDescriptor(); + + public abstract ParameterValue createValue(StaplerRequest req, JSONObject jo); + + /** + * Returns default parameter value for this definition. + * + * @return default parameter value or null if no defaults are available + * @since 1.253 + */ + public ParameterValue getDefaultParameterValue() { + return null; + } + + /** + * A list of available parameter definition types + */ + public static final DescriptorList LIST = new DescriptorList(); + + public abstract static class ParameterDescriptor extends + Descriptor { + + protected ParameterDescriptor(Class klazz) { + super(klazz); + } + + public String getValuePage() { + return getViewPage(clazz, "index.jelly"); + } + + @Override + public String getDisplayName() { + return "Parameter"; + } + + } + +} diff --git a/core/src/main/java/hudson/model/ParameterValue.java b/core/src/main/java/hudson/model/ParameterValue.java index 06dec8f6a7f3f330d19334186fd661530d53548f..a714274b493c11ed034ff1afa5c0de8476b32c32 100644 --- a/core/src/main/java/hudson/model/ParameterValue.java +++ b/core/src/main/java/hudson/model/ParameterValue.java @@ -1,129 +1,129 @@ -package hudson.model; - -import hudson.tasks.BuildWrapper; -import hudson.tasks.Builder; -import hudson.util.VariableResolver; - -import java.util.Map; - -import org.kohsuke.stapler.StaplerRequest; -import net.sf.json.JSONObject; - -/** - * A value for a parameter in a build. - * - * Created by {@link ParameterDefinition#createValue(StaplerRequest, JSONObject)} for - * a particular build (although this 'owner' build object is passed in for every method - * call as a parameter so that the parameter won't have to persist it.) - * - *

Persistence

- *

- * Instances of {@link ParameterValue}s are persisted into build's build.xml - * through XStream (via {@link ParametersAction}), so instances need to be persistable. - * - *

Assocaited Views

- *

value.jelly

- * The value.jelly view contributes a UI fragment to display the parameter - * values used for a build. - * - *

Notes

- *
    - *
  1. {@link ParameterValue} is used to record values of the past build, but - * {@link ParameterDefinition} used back then might be gone already, or represent - * a different parameter now. So don't try to use the name to infer - * {@link ParameterDefinition} is. - *
- * @see ParameterDefinition - */ -public abstract class ParameterValue { - protected final String name; - - protected ParameterValue(String name) { - this.name = name; - } - - /** - * Name of the parameter. - * - * This uniquely distinguishes {@link ParameterValue} among other parameters - * for the same build. This must be the same as {@link ParameterDefinition#getName()}. - */ - public final String getName() { - return name; - } - - /** - * Adds environmental variables for the builds to the given map. - * - *

- * This provides a means for a parameter to pass the parameter - * values to the build to be performed. - * - *

- * When this method is invoked, the map already contains the - * current "planned export" list. The implementation is - * expected to add more values to this map (or do nothing) - * - *

- * Environment variables should be by convention all upper case. - * (This is so that a Windows/Unix heterogenous environment - * won't get inconsistent result depending on which platform to - * execute.) - * - * @param env - * never null. - * @param build - * The build for which this parameter is being used. Never null. - */ - public void buildEnvVars(AbstractBuild build, Map env) { - // no-op by default - } - - /** - * Called at the beginning of a build to let a {@link ParameterValue} - * contributes a {@link BuildWrapper} to the build. - * - *

- * This provides a means for a parameter to perform more extensive - * set up / tear down during a build. - * - * @param build - * The build for which this parameter is being used. Never null. - * @return - * null if the parameter has no {@link BuildWrapper} to contribute to. - */ - public BuildWrapper createBuildWrapper(AbstractBuild build) { - return null; - } - - /** - * Returns a {@link VariableResolver} so that other components like {@link Builder}s - * can perform variable substitution to reflect parameter values into the build process. - * - * createVariableResolver(AbstractBuild build) { - return VariableResolver.NONE; - } - - /** - * Accessing {@link ParameterDefinition} is not a good idea. - * - * @deprecated - * parameter definition may change any time. So if you find yourself - * in need of accessing the information from {@link ParameterDefinition}, - * instead copy them in {@link ParameterDefinition#createValue(StaplerRequest, JSONObject)} - * into {@link ParameterValue}. - */ - public ParameterDefinition getDefinition() { - throw new UnsupportedOperationException(); - } -} +package hudson.model; + +import hudson.tasks.BuildWrapper; +import hudson.tasks.Builder; +import hudson.util.VariableResolver; + +import java.util.Map; + +import org.kohsuke.stapler.StaplerRequest; +import net.sf.json.JSONObject; + +/** + * A value for a parameter in a build. + * + * Created by {@link ParameterDefinition#createValue(StaplerRequest, JSONObject)} for + * a particular build (although this 'owner' build object is passed in for every method + * call as a parameter so that the parameter won't have to persist it.) + * + *

Persistence

+ *

+ * Instances of {@link ParameterValue}s are persisted into build's build.xml + * through XStream (via {@link ParametersAction}), so instances need to be persistable. + * + *

Assocaited Views

+ *

value.jelly

+ * The value.jelly view contributes a UI fragment to display the parameter + * values used for a build. + * + *

Notes

+ *
    + *
  1. {@link ParameterValue} is used to record values of the past build, but + * {@link ParameterDefinition} used back then might be gone already, or represent + * a different parameter now. So don't try to use the name to infer + * {@link ParameterDefinition} is. + *
+ * @see ParameterDefinition + */ +public abstract class ParameterValue { + protected final String name; + + protected ParameterValue(String name) { + this.name = name; + } + + /** + * Name of the parameter. + * + * This uniquely distinguishes {@link ParameterValue} among other parameters + * for the same build. This must be the same as {@link ParameterDefinition#getName()}. + */ + public final String getName() { + return name; + } + + /** + * Adds environmental variables for the builds to the given map. + * + *

+ * This provides a means for a parameter to pass the parameter + * values to the build to be performed. + * + *

+ * When this method is invoked, the map already contains the + * current "planned export" list. The implementation is + * expected to add more values to this map (or do nothing) + * + *

+ * Environment variables should be by convention all upper case. + * (This is so that a Windows/Unix heterogenous environment + * won't get inconsistent result depending on which platform to + * execute.) + * + * @param env + * never null. + * @param build + * The build for which this parameter is being used. Never null. + */ + public void buildEnvVars(AbstractBuild build, Map env) { + // no-op by default + } + + /** + * Called at the beginning of a build to let a {@link ParameterValue} + * contributes a {@link BuildWrapper} to the build. + * + *

+ * This provides a means for a parameter to perform more extensive + * set up / tear down during a build. + * + * @param build + * The build for which this parameter is being used. Never null. + * @return + * null if the parameter has no {@link BuildWrapper} to contribute to. + */ + public BuildWrapper createBuildWrapper(AbstractBuild build) { + return null; + } + + /** + * Returns a {@link VariableResolver} so that other components like {@link Builder}s + * can perform variable substitution to reflect parameter values into the build process. + * + * createVariableResolver(AbstractBuild build) { + return VariableResolver.NONE; + } + + /** + * Accessing {@link ParameterDefinition} is not a good idea. + * + * @deprecated + * parameter definition may change any time. So if you find yourself + * in need of accessing the information from {@link ParameterDefinition}, + * instead copy them in {@link ParameterDefinition#createValue(StaplerRequest, JSONObject)} + * into {@link ParameterValue}. + */ + public ParameterDefinition getDefinition() { + throw new UnsupportedOperationException(); + } +} diff --git a/core/src/main/java/hudson/model/ParameterizedProjectTask.java b/core/src/main/java/hudson/model/ParameterizedProjectTask.java index 9f3a2314870d3c7db9bdb989fe5d22c74b8ca818..4b5176b4c229b6a29b7daf969b8dc28ff324f987 100644 --- a/core/src/main/java/hudson/model/ParameterizedProjectTask.java +++ b/core/src/main/java/hudson/model/ParameterizedProjectTask.java @@ -1,62 +1,62 @@ -package hudson.model; - -import hudson.model.Queue.Executable; -import hudson.util.QueueTaskFilter; - -import java.io.IOException; -import java.util.List; - -/** - * A task representing a project that should be built with a certain set of - * parameter values - */ -public class ParameterizedProjectTask extends QueueTaskFilter { - - private final AbstractProject project; - private final List parameters; - - public ParameterizedProjectTask(AbstractProject project, List parameters) { - super(project); - this.project = project; - this.parameters = parameters; - } - - @Override - public Executable createExecutable() throws IOException { - AbstractBuild build = project.createExecutable(); - build.addAction(new ParametersAction(parameters, build)); - - return build; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((parameters == null) ? 0 : parameters.hashCode()); - result = prime * result + ((project == null) ? 0 : project.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ParameterizedProjectTask other = (ParameterizedProjectTask) obj; - if (parameters == null) { - if (other.parameters != null) - return false; - } else if (!parameters.equals(other.parameters)) { - return false; - } - if (project != other.project) { - return false; - } - return true; - } -} +package hudson.model; + +import hudson.model.Queue.Executable; +import hudson.util.QueueTaskFilter; + +import java.io.IOException; +import java.util.List; + +/** + * A task representing a project that should be built with a certain set of + * parameter values + */ +public class ParameterizedProjectTask extends QueueTaskFilter { + + private final AbstractProject project; + private final List parameters; + + public ParameterizedProjectTask(AbstractProject project, List parameters) { + super(project); + this.project = project; + this.parameters = parameters; + } + + @Override + public Executable createExecutable() throws IOException { + AbstractBuild build = project.createExecutable(); + build.addAction(new ParametersAction(parameters, build)); + + return build; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((parameters == null) ? 0 : parameters.hashCode()); + result = prime * result + ((project == null) ? 0 : project.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ParameterizedProjectTask other = (ParameterizedProjectTask) obj; + if (parameters == null) { + if (other.parameters != null) + return false; + } else if (!parameters.equals(other.parameters)) { + return false; + } + if (project != other.project) { + return false; + } + return true; + } +} diff --git a/core/src/main/java/hudson/model/ParametersAction.java b/core/src/main/java/hudson/model/ParametersAction.java index d70ea737af89b51ef5ff132adbd1c17b53c0fd7b..717a937908e52b9c9de9d7b6d333876300a5d50a 100644 --- a/core/src/main/java/hudson/model/ParametersAction.java +++ b/core/src/main/java/hudson/model/ParametersAction.java @@ -1,88 +1,88 @@ -package hudson.model; - -import hudson.Util; -import hudson.tasks.BuildWrapper; -import hudson.util.VariableResolver; - -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -/** - * Records the parameter values used for a build. - * - *

- * This object is associated with the build record so that we remember what parameters - * were used for what build. - */ -public class ParametersAction implements Action, Iterable { - - private final List parameters; - private final AbstractBuild build; - - public ParametersAction(List parameters, AbstractBuild build) { - this.parameters = parameters; - this.build = build; - } - - public void createBuildWrappers(AbstractBuild build, Collection result) { - for (ParameterValue p : parameters) { - BuildWrapper w = p.createBuildWrapper(build); - if(w!=null) result.add(w); - } - } - - public void buildEnvVars(AbstractBuild build, Map env) { - for (ParameterValue p : parameters) - p.buildEnvVars(build,env); - } - - /** - * Performs a variable subsitution to the given text and return it. - */ - public String substitute(AbstractBuild build, String text) { - return Util.replaceMacro(text,createVariableResolver(build)); - } - - /** - * Creates an {@link VariableResolver} that aggregates all the parameters. - */ - public VariableResolver createVariableResolver(AbstractBuild build) { - VariableResolver[] resolvers = new VariableResolver[parameters.size()+1]; - int i=0; - for (ParameterValue p : parameters) - resolvers[i++] = p.createVariableResolver(build); - - resolvers[i] = build.getBuildVariableResolver(); - - return new VariableResolver.Union(resolvers); - } - - public AbstractBuild getBuild() { - return build; - } - - public Iterator iterator() { - return parameters.iterator(); - } - - public List getParameters() { - return parameters; - } - - @Override - public String getDisplayName() { - return Messages.ParameterAction_DisplayName(); - } - - @Override - public String getIconFileName() { - return "document-properties.gif"; - } - - @Override - public String getUrlName() { - return "parameters"; - } -} +package hudson.model; + +import hudson.Util; +import hudson.tasks.BuildWrapper; +import hudson.util.VariableResolver; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Records the parameter values used for a build. + * + *

+ * This object is associated with the build record so that we remember what parameters + * were used for what build. + */ +public class ParametersAction implements Action, Iterable { + + private final List parameters; + private final AbstractBuild build; + + public ParametersAction(List parameters, AbstractBuild build) { + this.parameters = parameters; + this.build = build; + } + + public void createBuildWrappers(AbstractBuild build, Collection result) { + for (ParameterValue p : parameters) { + BuildWrapper w = p.createBuildWrapper(build); + if(w!=null) result.add(w); + } + } + + public void buildEnvVars(AbstractBuild build, Map env) { + for (ParameterValue p : parameters) + p.buildEnvVars(build,env); + } + + /** + * Performs a variable subsitution to the given text and return it. + */ + public String substitute(AbstractBuild build, String text) { + return Util.replaceMacro(text,createVariableResolver(build)); + } + + /** + * Creates an {@link VariableResolver} that aggregates all the parameters. + */ + public VariableResolver createVariableResolver(AbstractBuild build) { + VariableResolver[] resolvers = new VariableResolver[parameters.size()+1]; + int i=0; + for (ParameterValue p : parameters) + resolvers[i++] = p.createVariableResolver(build); + + resolvers[i] = build.getBuildVariableResolver(); + + return new VariableResolver.Union(resolvers); + } + + public AbstractBuild getBuild() { + return build; + } + + public Iterator iterator() { + return parameters.iterator(); + } + + public List getParameters() { + return parameters; + } + + @Override + public String getDisplayName() { + return Messages.ParameterAction_DisplayName(); + } + + @Override + public String getIconFileName() { + return "document-properties.gif"; + } + + @Override + public String getUrlName() { + return "parameters"; + } +} diff --git a/core/src/main/java/hudson/model/ParametersDefinitionProperty.java b/core/src/main/java/hudson/model/ParametersDefinitionProperty.java index a1e8bbe8db3351fc49eb26f899e4d7c0d4f35172..bf60e381ad8f4894abea55785d6845b9f5178abc 100644 --- a/core/src/main/java/hudson/model/ParametersDefinitionProperty.java +++ b/core/src/main/java/hudson/model/ParametersDefinitionProperty.java @@ -1,153 +1,153 @@ -package hudson.model; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import net.sf.json.JSONArray; -import net.sf.json.JSONObject; - -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; - -import javax.servlet.ServletException; - -/** - * Keeps a list of the parameters defined for a project. - * - *

- * This class also implements {@link Action} so that index.jelly provides - * a form to enter build parameters. - */ -public class ParametersDefinitionProperty extends JobProperty> - implements Action { - - private final List parameterDefinitions; - - public ParametersDefinitionProperty(List parameterDefinitions) { - this.parameterDefinitions = parameterDefinitions; - } - - public AbstractProject getOwner() { - return owner; - } - - public List getParameterDefinitions() { - return parameterDefinitions; - } - - @Override - public Action getJobAction(AbstractProject job) { - return this; - } - - public AbstractProject getProject() { - return (AbstractProject) owner; - } - - /** - * Interprets the form submission and schedules a build for a parameterized job. - * - *

- * This method is supposed to be invoked from {@link AbstractProject#doBuild(StaplerRequest, StaplerResponse)}. - */ - public void _doBuild(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - if(!req.getMethod().equals("POST")) { - // show the parameter entry form. - req.getView(this,"index.jelly").forward(req,rsp); - return; - } - - List values = new ArrayList(); - - JSONObject formData = req.getSubmittedForm(); - JSONArray a = JSONArray.fromObject(formData.get("parameter")); - - for (Object o : a) { - JSONObject jo = (JSONObject) o; - String name = jo.getString("name"); - - ParameterDefinition d = getParameterDefinition(name); - if(d==null) - throw new IllegalArgumentException("No such parameter definition: " + name); - values.add(d.createValue(req, jo)); - } - - Hudson.getInstance().getQueue().add( - new ParameterizedProjectTask(owner, values), 0); - - // send the user back to the job top page. - rsp.sendRedirect("."); - } - - /** - * Gets the {@link ParameterDefinition} of the given name, if any. - */ - public ParameterDefinition getParameterDefinition(String name) { - for (ParameterDefinition pd : parameterDefinitions) - if (pd.getName().equals(name)) - return pd; - return null; - } - - @Override - public JobPropertyDescriptor getDescriptor() { - return DESCRIPTOR; - } - - public static final JobPropertyDescriptor DESCRIPTOR = new DescriptorImpl(); - - public static class DescriptorImpl extends JobPropertyDescriptor { - - protected DescriptorImpl() { - super(ParametersDefinitionProperty.class); - } - - @Override - public boolean isApplicable(Class jobType) { - return AbstractProject.class.isAssignableFrom(jobType); - } - - @Override - public JobProperty newInstance(StaplerRequest req, - JSONObject formData) throws FormException { - if (formData.isNullObject()) { - return null; - } - - List parameterDefinitions = Descriptor.newInstancesFromHeteroList( - req, formData, "parameter", ParameterDefinition.LIST); - if(parameterDefinitions.isEmpty()) - return null; - - return new ParametersDefinitionProperty(parameterDefinitions); - } - - @Override - public String getDisplayName() { - return Messages.ParametersDefinitionProperty_DisplayName(); - } - - } - - @Override - public String getDisplayName() { - return null; - } - - @Override - public String getIconFileName() { - return null; - } - - @Override - public String getUrlName() { - return "parameters"; - } - - - static { - ParameterDefinition.LIST.add(StringParameterDefinition.DESCRIPTOR); - ParameterDefinition.LIST.add(FileParameterDefinition.DESCRIPTOR); - } -} +package hudson.model; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import net.sf.json.JSONArray; +import net.sf.json.JSONObject; + +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; + +import javax.servlet.ServletException; + +/** + * Keeps a list of the parameters defined for a project. + * + *

+ * This class also implements {@link Action} so that index.jelly provides + * a form to enter build parameters. + */ +public class ParametersDefinitionProperty extends JobProperty> + implements Action { + + private final List parameterDefinitions; + + public ParametersDefinitionProperty(List parameterDefinitions) { + this.parameterDefinitions = parameterDefinitions; + } + + public AbstractProject getOwner() { + return owner; + } + + public List getParameterDefinitions() { + return parameterDefinitions; + } + + @Override + public Action getJobAction(AbstractProject job) { + return this; + } + + public AbstractProject getProject() { + return (AbstractProject) owner; + } + + /** + * Interprets the form submission and schedules a build for a parameterized job. + * + *

+ * This method is supposed to be invoked from {@link AbstractProject#doBuild(StaplerRequest, StaplerResponse)}. + */ + public void _doBuild(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { + if(!req.getMethod().equals("POST")) { + // show the parameter entry form. + req.getView(this,"index.jelly").forward(req,rsp); + return; + } + + List values = new ArrayList(); + + JSONObject formData = req.getSubmittedForm(); + JSONArray a = JSONArray.fromObject(formData.get("parameter")); + + for (Object o : a) { + JSONObject jo = (JSONObject) o; + String name = jo.getString("name"); + + ParameterDefinition d = getParameterDefinition(name); + if(d==null) + throw new IllegalArgumentException("No such parameter definition: " + name); + values.add(d.createValue(req, jo)); + } + + Hudson.getInstance().getQueue().add( + new ParameterizedProjectTask(owner, values), 0); + + // send the user back to the job top page. + rsp.sendRedirect("."); + } + + /** + * Gets the {@link ParameterDefinition} of the given name, if any. + */ + public ParameterDefinition getParameterDefinition(String name) { + for (ParameterDefinition pd : parameterDefinitions) + if (pd.getName().equals(name)) + return pd; + return null; + } + + @Override + public JobPropertyDescriptor getDescriptor() { + return DESCRIPTOR; + } + + public static final JobPropertyDescriptor DESCRIPTOR = new DescriptorImpl(); + + public static class DescriptorImpl extends JobPropertyDescriptor { + + protected DescriptorImpl() { + super(ParametersDefinitionProperty.class); + } + + @Override + public boolean isApplicable(Class jobType) { + return AbstractProject.class.isAssignableFrom(jobType); + } + + @Override + public JobProperty newInstance(StaplerRequest req, + JSONObject formData) throws FormException { + if (formData.isNullObject()) { + return null; + } + + List parameterDefinitions = Descriptor.newInstancesFromHeteroList( + req, formData, "parameter", ParameterDefinition.LIST); + if(parameterDefinitions.isEmpty()) + return null; + + return new ParametersDefinitionProperty(parameterDefinitions); + } + + @Override + public String getDisplayName() { + return Messages.ParametersDefinitionProperty_DisplayName(); + } + + } + + @Override + public String getDisplayName() { + return null; + } + + @Override + public String getIconFileName() { + return null; + } + + @Override + public String getUrlName() { + return "parameters"; + } + + + static { + ParameterDefinition.LIST.add(StringParameterDefinition.DESCRIPTOR); + ParameterDefinition.LIST.add(FileParameterDefinition.DESCRIPTOR); + } +} diff --git a/core/src/main/java/hudson/model/RunParameterDefinition.java b/core/src/main/java/hudson/model/RunParameterDefinition.java index fcd1ce702331d246e99a1a6d9a8ae184a7d89a1b..668e4708ed78f6dabfe96399688f0e08c793f2c4 100644 --- a/core/src/main/java/hudson/model/RunParameterDefinition.java +++ b/core/src/main/java/hudson/model/RunParameterDefinition.java @@ -1,45 +1,45 @@ -package hudson.model; - -import net.sf.json.JSONObject; - -import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.StaplerRequest; - -public class RunParameterDefinition extends ParameterDefinition { - - @DataBoundConstructor - public RunParameterDefinition(String name) { - super(name); - } - - @Override - public ParameterDescriptor getDescriptor() { - return DESCRIPTOR; - } - - public static final ParameterDescriptor DESCRIPTOR = new DescriptorImpl(); - - public static class DescriptorImpl extends ParameterDescriptor { - - protected DescriptorImpl() { - super(RunParameterDefinition.class); - } - - @Override - public String getDisplayName() { - return "Run Parameter"; - } - - @Override - public ParameterDefinition newInstance(StaplerRequest req, JSONObject formData) throws FormException { - return req.bindJSON(RunParameterDefinition.class, formData); - } - - } - - @Override - public ParameterValue createValue(StaplerRequest req, JSONObject jo) { - return req.bindJSON(RunParameterValue.class, jo); - } - -} +package hudson.model; + +import net.sf.json.JSONObject; + +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.StaplerRequest; + +public class RunParameterDefinition extends ParameterDefinition { + + @DataBoundConstructor + public RunParameterDefinition(String name) { + super(name); + } + + @Override + public ParameterDescriptor getDescriptor() { + return DESCRIPTOR; + } + + public static final ParameterDescriptor DESCRIPTOR = new DescriptorImpl(); + + public static class DescriptorImpl extends ParameterDescriptor { + + protected DescriptorImpl() { + super(RunParameterDefinition.class); + } + + @Override + public String getDisplayName() { + return "Run Parameter"; + } + + @Override + public ParameterDefinition newInstance(StaplerRequest req, JSONObject formData) throws FormException { + return req.bindJSON(RunParameterDefinition.class, formData); + } + + } + + @Override + public ParameterValue createValue(StaplerRequest req, JSONObject jo) { + return req.bindJSON(RunParameterValue.class, jo); + } + +} diff --git a/core/src/main/java/hudson/model/RunParameterValue.java b/core/src/main/java/hudson/model/RunParameterValue.java index d278e8da63875a41c45ec3f383d2085d3ec22097..857e079c4574efd1a97459d376c5961037bd2c2a 100644 --- a/core/src/main/java/hudson/model/RunParameterValue.java +++ b/core/src/main/java/hudson/model/RunParameterValue.java @@ -1,25 +1,25 @@ -package hudson.model; - -import org.kohsuke.stapler.DataBoundConstructor; - -import java.util.Map; - -public class RunParameterValue extends ParameterValue { - - public final Run run; - - @DataBoundConstructor - public RunParameterValue(String name, Run run) { - super(name); - this.run = run; - } - - /** - * Exposes the name/value as an environment variable. - */ - @Override - public void buildEnvVars(AbstractBuild build, Map env) { - // TODO: check with Tom if this is really what he had in mind - env.put(name.toUpperCase(),run.toString()); - } -} +package hudson.model; + +import org.kohsuke.stapler.DataBoundConstructor; + +import java.util.Map; + +public class RunParameterValue extends ParameterValue { + + public final Run run; + + @DataBoundConstructor + public RunParameterValue(String name, Run run) { + super(name); + this.run = run; + } + + /** + * Exposes the name/value as an environment variable. + */ + @Override + public void buildEnvVars(AbstractBuild build, Map env) { + // TODO: check with Tom if this is really what he had in mind + env.put(name.toUpperCase(),run.toString()); + } +} diff --git a/core/src/main/java/hudson/model/StringParameterDefinition.java b/core/src/main/java/hudson/model/StringParameterDefinition.java index 3e727e92b02172eaa2838852972842041db7a17f..0eedf79431614a1c1a0ed88137b7a48a1e49eeac 100644 --- a/core/src/main/java/hudson/model/StringParameterDefinition.java +++ b/core/src/main/java/hudson/model/StringParameterDefinition.java @@ -1,62 +1,62 @@ -package hudson.model; - -import net.sf.json.JSONObject; - -import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.StaplerRequest; - -/** - * Parameter whose value is a string value. - */ -public class StringParameterDefinition extends ParameterDefinition { - - private String defaultValue; - - @DataBoundConstructor - public StringParameterDefinition(String name, String defaultValue) { - super(name); - this.defaultValue = defaultValue; - } - - @Override - public ParameterDescriptor getDescriptor() { - return DESCRIPTOR; - } - - public String getDefaultValue() { - return defaultValue; - } - - public void setDefaultValue(String defaultValue) { - this.defaultValue = defaultValue; - } - - public StringParameterValue getDefaultParameterValue() { - return new StringParameterValue(getName(), defaultValue); - } - - public static final ParameterDescriptor DESCRIPTOR = new DescriptorImpl(); - - public static class DescriptorImpl extends ParameterDescriptor { - - protected DescriptorImpl() { - super(StringParameterDefinition.class); - } - - @Override - public String getDisplayName() { - return Messages.StringParameterDefinition_DisplayName(); - } - - @Override - public String getHelpFile() { - return "/help/parameter/string.html"; - } - } - - @Override - public ParameterValue createValue(StaplerRequest req, JSONObject jo) { - return req.bindJSON(StringParameterValue.class, jo); - } - -} +package hudson.model; + +import net.sf.json.JSONObject; + +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.StaplerRequest; + +/** + * Parameter whose value is a string value. + */ +public class StringParameterDefinition extends ParameterDefinition { + + private String defaultValue; + + @DataBoundConstructor + public StringParameterDefinition(String name, String defaultValue) { + super(name); + this.defaultValue = defaultValue; + } + + @Override + public ParameterDescriptor getDescriptor() { + return DESCRIPTOR; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + public StringParameterValue getDefaultParameterValue() { + return new StringParameterValue(getName(), defaultValue); + } + + public static final ParameterDescriptor DESCRIPTOR = new DescriptorImpl(); + + public static class DescriptorImpl extends ParameterDescriptor { + + protected DescriptorImpl() { + super(StringParameterDefinition.class); + } + + @Override + public String getDisplayName() { + return Messages.StringParameterDefinition_DisplayName(); + } + + @Override + public String getHelpFile() { + return "/help/parameter/string.html"; + } + } + + @Override + public ParameterValue createValue(StaplerRequest req, JSONObject jo) { + return req.bindJSON(StringParameterValue.class, jo); + } + +} diff --git a/core/src/main/java/hudson/model/StringParameterValue.java b/core/src/main/java/hudson/model/StringParameterValue.java index 6e09bbed41a8dd3c7a063bf0ed422c567b568f72..3f3c6d63a7dbbc358e5391cdf8f9732674941a49 100644 --- a/core/src/main/java/hudson/model/StringParameterValue.java +++ b/core/src/main/java/hudson/model/StringParameterValue.java @@ -1,42 +1,42 @@ -package hudson.model; - -import org.kohsuke.stapler.DataBoundConstructor; - -import java.util.Map; - -import hudson.util.VariableResolver; - -/** - * {@link ParameterValue} created from {@link StringParameterDefinition}. - */ -public class StringParameterValue extends ParameterValue { - public final String value; - - @DataBoundConstructor - public StringParameterValue(String name, String value) { - super(name); - this.value = value; - } - - /** - * Exposes the name/value as an environment variable. - */ - @Override - public void buildEnvVars(AbstractBuild build, Map env) { - env.put(name.toUpperCase(),value); - } - - @Override - public VariableResolver createVariableResolver(AbstractBuild build) { - return new VariableResolver() { - public String resolve(String name) { - return StringParameterValue.this.name.equals(name) ? value : null; - } - }; - } - - @Override - public String toString() { - return "(StringParameterValue) " + getName() + "='" + value + "'"; - } -} +package hudson.model; + +import org.kohsuke.stapler.DataBoundConstructor; + +import java.util.Map; + +import hudson.util.VariableResolver; + +/** + * {@link ParameterValue} created from {@link StringParameterDefinition}. + */ +public class StringParameterValue extends ParameterValue { + public final String value; + + @DataBoundConstructor + public StringParameterValue(String name, String value) { + super(name); + this.value = value; + } + + /** + * Exposes the name/value as an environment variable. + */ + @Override + public void buildEnvVars(AbstractBuild build, Map env) { + env.put(name.toUpperCase(),value); + } + + @Override + public VariableResolver createVariableResolver(AbstractBuild build) { + return new VariableResolver() { + public String resolve(String name) { + return StringParameterValue.this.name.equals(name) ? value : null; + } + }; + } + + @Override + public String toString() { + return "(StringParameterValue) " + getName() + "='" + value + "'"; + } +} diff --git a/core/src/main/java/hudson/security/AccessControlled.java b/core/src/main/java/hudson/security/AccessControlled.java index fe8de236c2801b794136ec24bc809a5ce90f2ce1..734e2d664e20fbd6e0d02e2932b6344583e70332 100644 --- a/core/src/main/java/hudson/security/AccessControlled.java +++ b/core/src/main/java/hudson/security/AccessControlled.java @@ -1,27 +1,27 @@ -package hudson.security; - -/** - * Object that has an {@link ACL} - * - * @since 1.220 - * @see http://hudson.gotdns.com/wiki/display/HUDSON/Making+your+plugin+behave+in+secured+Hudson - */ -public interface AccessControlled { - /** - * Obtains the ACL associated with this object. - * - * @return never null. - */ - ACL getACL(); - - /** - * Convenient short-cut for {@code getACL().checkPermission(permission)} - */ - void checkPermission(Permission permission); - - /** - * Convenient short-cut for {@code getACL().hasPermission(permission)} - */ - boolean hasPermission(Permission permission); - -} +package hudson.security; + +/** + * Object that has an {@link ACL} + * + * @since 1.220 + * @see http://hudson.gotdns.com/wiki/display/HUDSON/Making+your+plugin+behave+in+secured+Hudson + */ +public interface AccessControlled { + /** + * Obtains the ACL associated with this object. + * + * @return never null. + */ + ACL getACL(); + + /** + * Convenient short-cut for {@code getACL().checkPermission(permission)} + */ + void checkPermission(Permission permission); + + /** + * Convenient short-cut for {@code getACL().hasPermission(permission)} + */ + boolean hasPermission(Permission permission); + +} diff --git a/core/src/main/java/hudson/security/AuthorizationMatrixProperty.java b/core/src/main/java/hudson/security/AuthorizationMatrixProperty.java index 154d7c741d8a92bf2f3c8b4e506c7dfabf7393c3..7337869574ee6f940d7ea5af2401b1def958ca80 100644 --- a/core/src/main/java/hudson/security/AuthorizationMatrixProperty.java +++ b/core/src/main/java/hudson/security/AuthorizationMatrixProperty.java @@ -1,257 +1,257 @@ -package hudson.security; - -import hudson.model.Item; -import hudson.model.Job; -import hudson.model.JobProperty; -import hudson.model.JobPropertyDescriptor; -import hudson.model.Hudson; -import hudson.model.Run; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; - -import net.sf.json.JSONObject; - -import org.acegisecurity.Authentication; -import org.acegisecurity.acls.sid.GrantedAuthoritySid; -import org.acegisecurity.acls.sid.PrincipalSid; -import org.acegisecurity.acls.sid.Sid; -import org.kohsuke.stapler.StaplerRequest; - -import com.thoughtworks.xstream.converters.Converter; -import com.thoughtworks.xstream.converters.MarshallingContext; -import com.thoughtworks.xstream.converters.UnmarshallingContext; -import com.thoughtworks.xstream.io.HierarchicalStreamReader; -import com.thoughtworks.xstream.io.HierarchicalStreamWriter; - -/** - * {@link JobProperty} to associate ACL for each project. - */ -public class AuthorizationMatrixProperty extends JobProperty> { - - public static final JobPropertyDescriptor DESCRIPTOR = new DescriptorImpl(); - - private transient SidACL acl = new AclImpl(); - - private boolean useProjectSecurity; - - public boolean isUseProjectSecurity() { - return useProjectSecurity; - } - - public void setUseProjectSecurity(boolean useProjectSecurity) { - this.useProjectSecurity = useProjectSecurity; - } - - /** - * List up all permissions that are granted. - * - * Strings are either the granted authority or the principal, which is not - * distinguished. - */ - private final Map> grantedPermissions = new HashMap>(); - - private Set sids = new HashSet(); - - public Set getGroups() { - return sids; - } - - /** - * Returns all SIDs configured in this matrix, minus "anonymous" - * - * @return Always non-null. - */ - public List getAllSIDs() { - Set r = new HashSet(); - for (Set set : grantedPermissions.values()) - r.addAll(set); - r.remove("anonymous"); - - String[] data = r.toArray(new String[r.size()]); - Arrays.sort(data); - return Arrays.asList(data); - } - - /** - * Adds to {@link #grantedPermissions}. Use of this method should be limited - * during construction, as this object itself is considered immutable once - * populated. - */ - protected void add(Permission p, String sid) { - Set set = grantedPermissions.get(p); - if (set == null) - grantedPermissions.put(p, set = new HashSet()); - set.add(sid); - sids.add(sid); - } - - @Override - public JobPropertyDescriptor getDescriptor() { - return DESCRIPTOR; - } - - public static class DescriptorImpl extends JobPropertyDescriptor { - - @Override - public JobProperty newInstance(StaplerRequest req, - JSONObject formData) throws FormException { - boolean useProjectSecurity = formData.has("useProjectSecurity"); - AuthorizationMatrixProperty amp = new AuthorizationMatrixProperty(); - amp.setUseProjectSecurity(useProjectSecurity); - for (Map.Entry r : (Set>) formData - .getJSONObject("data").entrySet()) { - String sid = r.getKey(); - if (r.getValue() instanceof JSONObject) { - for (Map.Entry e : (Set>) ((JSONObject) r - .getValue()).entrySet()) { - if (e.getValue()) { - Permission p = Permission.fromId(e.getKey()); - amp.add(p, sid); - } - } - } - } - return amp; - } - - protected DescriptorImpl() { - super(AuthorizationMatrixProperty.class); - } - - @Override - public boolean isApplicable(Class jobType) { - // only applicable when ProjectMatrixAuthorizationStrategy is in charge - return Hudson.getInstance().getAuthorizationStrategy() instanceof ProjectMatrixAuthorizationStrategy; - } - - @Override - public String getDisplayName() { - return "Authorization Matrix"; - } - - public List getAllGroups() { - return Arrays.asList(PermissionGroup.get(Item.class),PermissionGroup.get(Run.class)); - } - - public boolean showPermission(Permission p) { - return p!=Item.CREATE; - } - } - - private final class AclImpl extends SidACL { - protected Boolean hasPermission(Sid sid, Permission p) { - String s = toString(sid); - for (; p != null; p = p.impliedBy) { - Set set = grantedPermissions.get(p); - if (set != null && set.contains(s)) - return true; - } - return null; - } - - protected Boolean _hasPermission(Authentication a, Permission permission) { - Boolean b = super._hasPermission(a, permission); - // permissions granted to anonymous users are granted to everyone - if (b == null) - b = hasPermission(ANONYMOUS, permission); - return b; - } - - private String toString(Sid p) { - if (p instanceof GrantedAuthoritySid) - return ((GrantedAuthoritySid) p).getGrantedAuthority(); - if (p instanceof PrincipalSid) - return ((PrincipalSid) p).getPrincipal(); - // hmm... - return p.toString(); - } - } - - private Object readResolve() { - acl = new AclImpl(); - return this; - } - - public SidACL getACL() { - return acl; - } - - /** - * Checks if the given SID has the given permission. - */ - public boolean hasPermission(String sid, Permission p) { - for (; p != null; p = p.impliedBy) { - Set set = grantedPermissions.get(p); - if (set != null && set.contains(sid)) - return true; - } - return false; - } - - /** - * Works like {@link #add(Permission, String)} but takes both parameters - * from a single string of the form PERMISSIONID:sid - */ - private void add(String shortForm) { - int idx = shortForm.indexOf(':'); - add(Permission.fromId(shortForm.substring(0, idx)), shortForm - .substring(idx + 1)); - } - - /** - * Persist {@link ProjectMatrixAuthorizationStrategy} as a list of IDs that - * represent {@link ProjectMatrixAuthorizationStrategy#grantedPermissions}. - */ - public static final class ConverterImpl implements Converter { - public boolean canConvert(Class type) { - return type == AuthorizationMatrixProperty.class; - } - - public void marshal(Object source, HierarchicalStreamWriter writer, - MarshallingContext context) { - AuthorizationMatrixProperty amp = (AuthorizationMatrixProperty) source; - - writer.startNode("useProjectSecurity"); - context.convertAnother(Boolean.valueOf(amp.isUseProjectSecurity())); - writer.endNode(); - - for (Entry> e : amp.grantedPermissions - .entrySet()) { - String p = e.getKey().getId(); - for (String sid : e.getValue()) { - writer.startNode("permission"); - context.convertAnother(p + ':' + sid); - writer.endNode(); - } - } - - } - - public Object unmarshal(HierarchicalStreamReader reader, - final UnmarshallingContext context) { - AuthorizationMatrixProperty as = new AuthorizationMatrixProperty(); - - String prop = reader.peekNextChild(); - if (prop!=null && prop.equals("useProjectSecurity")) { - reader.moveDown(); - Boolean useSecurity = (Boolean) context.convertAnother(as, Boolean.class); - as.setUseProjectSecurity(useSecurity.booleanValue()); - reader.moveUp(); - } - while (reader.hasMoreChildren()) { - reader.moveDown(); - String id = (String) context.convertAnother(as, String.class); - as.add(id); - reader.moveUp(); - } - - return as; - } - } -} +package hudson.security; + +import hudson.model.Item; +import hudson.model.Job; +import hudson.model.JobProperty; +import hudson.model.JobPropertyDescriptor; +import hudson.model.Hudson; +import hudson.model.Run; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import net.sf.json.JSONObject; + +import org.acegisecurity.Authentication; +import org.acegisecurity.acls.sid.GrantedAuthoritySid; +import org.acegisecurity.acls.sid.PrincipalSid; +import org.acegisecurity.acls.sid.Sid; +import org.kohsuke.stapler.StaplerRequest; + +import com.thoughtworks.xstream.converters.Converter; +import com.thoughtworks.xstream.converters.MarshallingContext; +import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; + +/** + * {@link JobProperty} to associate ACL for each project. + */ +public class AuthorizationMatrixProperty extends JobProperty> { + + public static final JobPropertyDescriptor DESCRIPTOR = new DescriptorImpl(); + + private transient SidACL acl = new AclImpl(); + + private boolean useProjectSecurity; + + public boolean isUseProjectSecurity() { + return useProjectSecurity; + } + + public void setUseProjectSecurity(boolean useProjectSecurity) { + this.useProjectSecurity = useProjectSecurity; + } + + /** + * List up all permissions that are granted. + * + * Strings are either the granted authority or the principal, which is not + * distinguished. + */ + private final Map> grantedPermissions = new HashMap>(); + + private Set sids = new HashSet(); + + public Set getGroups() { + return sids; + } + + /** + * Returns all SIDs configured in this matrix, minus "anonymous" + * + * @return Always non-null. + */ + public List getAllSIDs() { + Set r = new HashSet(); + for (Set set : grantedPermissions.values()) + r.addAll(set); + r.remove("anonymous"); + + String[] data = r.toArray(new String[r.size()]); + Arrays.sort(data); + return Arrays.asList(data); + } + + /** + * Adds to {@link #grantedPermissions}. Use of this method should be limited + * during construction, as this object itself is considered immutable once + * populated. + */ + protected void add(Permission p, String sid) { + Set set = grantedPermissions.get(p); + if (set == null) + grantedPermissions.put(p, set = new HashSet()); + set.add(sid); + sids.add(sid); + } + + @Override + public JobPropertyDescriptor getDescriptor() { + return DESCRIPTOR; + } + + public static class DescriptorImpl extends JobPropertyDescriptor { + + @Override + public JobProperty newInstance(StaplerRequest req, + JSONObject formData) throws FormException { + boolean useProjectSecurity = formData.has("useProjectSecurity"); + AuthorizationMatrixProperty amp = new AuthorizationMatrixProperty(); + amp.setUseProjectSecurity(useProjectSecurity); + for (Map.Entry r : (Set>) formData + .getJSONObject("data").entrySet()) { + String sid = r.getKey(); + if (r.getValue() instanceof JSONObject) { + for (Map.Entry e : (Set>) ((JSONObject) r + .getValue()).entrySet()) { + if (e.getValue()) { + Permission p = Permission.fromId(e.getKey()); + amp.add(p, sid); + } + } + } + } + return amp; + } + + protected DescriptorImpl() { + super(AuthorizationMatrixProperty.class); + } + + @Override + public boolean isApplicable(Class jobType) { + // only applicable when ProjectMatrixAuthorizationStrategy is in charge + return Hudson.getInstance().getAuthorizationStrategy() instanceof ProjectMatrixAuthorizationStrategy; + } + + @Override + public String getDisplayName() { + return "Authorization Matrix"; + } + + public List getAllGroups() { + return Arrays.asList(PermissionGroup.get(Item.class),PermissionGroup.get(Run.class)); + } + + public boolean showPermission(Permission p) { + return p!=Item.CREATE; + } + } + + private final class AclImpl extends SidACL { + protected Boolean hasPermission(Sid sid, Permission p) { + String s = toString(sid); + for (; p != null; p = p.impliedBy) { + Set set = grantedPermissions.get(p); + if (set != null && set.contains(s)) + return true; + } + return null; + } + + protected Boolean _hasPermission(Authentication a, Permission permission) { + Boolean b = super._hasPermission(a, permission); + // permissions granted to anonymous users are granted to everyone + if (b == null) + b = hasPermission(ANONYMOUS, permission); + return b; + } + + private String toString(Sid p) { + if (p instanceof GrantedAuthoritySid) + return ((GrantedAuthoritySid) p).getGrantedAuthority(); + if (p instanceof PrincipalSid) + return ((PrincipalSid) p).getPrincipal(); + // hmm... + return p.toString(); + } + } + + private Object readResolve() { + acl = new AclImpl(); + return this; + } + + public SidACL getACL() { + return acl; + } + + /** + * Checks if the given SID has the given permission. + */ + public boolean hasPermission(String sid, Permission p) { + for (; p != null; p = p.impliedBy) { + Set set = grantedPermissions.get(p); + if (set != null && set.contains(sid)) + return true; + } + return false; + } + + /** + * Works like {@link #add(Permission, String)} but takes both parameters + * from a single string of the form PERMISSIONID:sid + */ + private void add(String shortForm) { + int idx = shortForm.indexOf(':'); + add(Permission.fromId(shortForm.substring(0, idx)), shortForm + .substring(idx + 1)); + } + + /** + * Persist {@link ProjectMatrixAuthorizationStrategy} as a list of IDs that + * represent {@link ProjectMatrixAuthorizationStrategy#grantedPermissions}. + */ + public static final class ConverterImpl implements Converter { + public boolean canConvert(Class type) { + return type == AuthorizationMatrixProperty.class; + } + + public void marshal(Object source, HierarchicalStreamWriter writer, + MarshallingContext context) { + AuthorizationMatrixProperty amp = (AuthorizationMatrixProperty) source; + + writer.startNode("useProjectSecurity"); + context.convertAnother(Boolean.valueOf(amp.isUseProjectSecurity())); + writer.endNode(); + + for (Entry> e : amp.grantedPermissions + .entrySet()) { + String p = e.getKey().getId(); + for (String sid : e.getValue()) { + writer.startNode("permission"); + context.convertAnother(p + ':' + sid); + writer.endNode(); + } + } + + } + + public Object unmarshal(HierarchicalStreamReader reader, + final UnmarshallingContext context) { + AuthorizationMatrixProperty as = new AuthorizationMatrixProperty(); + + String prop = reader.peekNextChild(); + if (prop!=null && prop.equals("useProjectSecurity")) { + reader.moveDown(); + Boolean useSecurity = (Boolean) context.convertAnother(as, Boolean.class); + as.setUseProjectSecurity(useSecurity.booleanValue()); + reader.moveUp(); + } + while (reader.hasMoreChildren()) { + reader.moveDown(); + String id = (String) context.convertAnother(as, String.class); + as.add(id); + reader.moveUp(); + } + + return as; + } + } +} diff --git a/core/src/main/java/hudson/security/ProjectMatrixAuthorizationStrategy.java b/core/src/main/java/hudson/security/ProjectMatrixAuthorizationStrategy.java index 6ff8c4f4208f8b5efc1bd5d7b52cfd10e599f264..2f47a5e2387300960f729bca3dc7e085adf92467 100644 --- a/core/src/main/java/hudson/security/ProjectMatrixAuthorizationStrategy.java +++ b/core/src/main/java/hudson/security/ProjectMatrixAuthorizationStrategy.java @@ -1,78 +1,78 @@ -package hudson.security; - -import hudson.model.AbstractProject; -import hudson.model.Descriptor; -import hudson.model.Jobs; -import hudson.util.RobustReflectionConverter; -import com.thoughtworks.xstream.io.HierarchicalStreamReader; -import com.thoughtworks.xstream.converters.UnmarshallingContext; -import com.thoughtworks.xstream.mapper.Mapper; -import com.thoughtworks.xstream.core.JVM; - -/** - * {@link GlobalMatrixAuthorizationStrategy} plus per-project ACL. - * - *

- * Per-project ACL is stored in {@link AuthorizationMatrixProperty}. - * - * @author Kohsuke Kawaguchi - */ -public class ProjectMatrixAuthorizationStrategy extends GlobalMatrixAuthorizationStrategy { - @Override - public ACL getACL(AbstractProject project) { - AuthorizationMatrixProperty amp = project.getProperty(AuthorizationMatrixProperty.class); - if (amp != null && amp.isUseProjectSecurity()) { - return amp.getACL().newInheritingACL(getRootACL()); - } else { - return getRootACL(); - } - } - - public Descriptor getDescriptor() { - return DESCRIPTOR; - } - - public static final Descriptor DESCRIPTOR = new DescriptorImpl(ProjectMatrixAuthorizationStrategy.class) { - @Override - protected GlobalMatrixAuthorizationStrategy create() { - return new ProjectMatrixAuthorizationStrategy(); - } - - @Override - public String getDisplayName() { - return Messages.ProjectMatrixAuthorizationStrategy_DisplayName(); - } - }; - - public static class ConverterImpl extends GlobalMatrixAuthorizationStrategy.ConverterImpl { - private RobustReflectionConverter ref; - - public ConverterImpl(Mapper m) { - ref = new RobustReflectionConverter(m,new JVM().bestReflectionProvider()); - } - - protected GlobalMatrixAuthorizationStrategy create() { - return new ProjectMatrixAuthorizationStrategy(); - } - - public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { - String name = reader.peekNextChild(); - if(name!=null && (name.equals("permission") || name.equals("useProjectSecurity"))) - // the proper serialization form - return super.unmarshal(reader, context); - else - // remain compatible with earlier problem where we used reflection converter - return ref.unmarshal(reader,context); - } - - public boolean canConvert(Class type) { - return type==ProjectMatrixAuthorizationStrategy.class; - } - } - - static { - LIST.add(DESCRIPTOR); - Jobs.PROPERTIES.add(AuthorizationMatrixProperty.DESCRIPTOR); - } -} - +package hudson.security; + +import hudson.model.AbstractProject; +import hudson.model.Descriptor; +import hudson.model.Jobs; +import hudson.util.RobustReflectionConverter; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.mapper.Mapper; +import com.thoughtworks.xstream.core.JVM; + +/** + * {@link GlobalMatrixAuthorizationStrategy} plus per-project ACL. + * + *

+ * Per-project ACL is stored in {@link AuthorizationMatrixProperty}. + * + * @author Kohsuke Kawaguchi + */ +public class ProjectMatrixAuthorizationStrategy extends GlobalMatrixAuthorizationStrategy { + @Override + public ACL getACL(AbstractProject project) { + AuthorizationMatrixProperty amp = project.getProperty(AuthorizationMatrixProperty.class); + if (amp != null && amp.isUseProjectSecurity()) { + return amp.getACL().newInheritingACL(getRootACL()); + } else { + return getRootACL(); + } + } + + public Descriptor getDescriptor() { + return DESCRIPTOR; + } + + public static final Descriptor DESCRIPTOR = new DescriptorImpl(ProjectMatrixAuthorizationStrategy.class) { + @Override + protected GlobalMatrixAuthorizationStrategy create() { + return new ProjectMatrixAuthorizationStrategy(); + } + + @Override + public String getDisplayName() { + return Messages.ProjectMatrixAuthorizationStrategy_DisplayName(); + } + }; + + public static class ConverterImpl extends GlobalMatrixAuthorizationStrategy.ConverterImpl { + private RobustReflectionConverter ref; + + public ConverterImpl(Mapper m) { + ref = new RobustReflectionConverter(m,new JVM().bestReflectionProvider()); + } + + protected GlobalMatrixAuthorizationStrategy create() { + return new ProjectMatrixAuthorizationStrategy(); + } + + public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + String name = reader.peekNextChild(); + if(name!=null && (name.equals("permission") || name.equals("useProjectSecurity"))) + // the proper serialization form + return super.unmarshal(reader, context); + else + // remain compatible with earlier problem where we used reflection converter + return ref.unmarshal(reader,context); + } + + public boolean canConvert(Class type) { + return type==ProjectMatrixAuthorizationStrategy.class; + } + } + + static { + LIST.add(DESCRIPTOR); + Jobs.PROPERTIES.add(AuthorizationMatrixProperty.DESCRIPTOR); + } +} + diff --git a/core/src/main/java/hudson/slaves/ComputerLauncher.java b/core/src/main/java/hudson/slaves/ComputerLauncher.java index df64b3a4a2846ca9d02ed3a4c3f9d27927bb42ed..fb588ec8ded893444dcd74109aeabb7029f14146 100644 --- a/core/src/main/java/hudson/slaves/ComputerLauncher.java +++ b/core/src/main/java/hudson/slaves/ComputerLauncher.java @@ -1,67 +1,67 @@ -package hudson.slaves; - -import hudson.ExtensionPoint; -import hudson.model.Computer; -import hudson.model.Describable; -import hudson.remoting.Channel.Listener; -import hudson.util.DescriptorList; -import hudson.util.StreamTaskListener; - -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Extension point to allow control over how {@link Computer}s are "launched", - * meaning how they get connected to their slave agent program. - * - *

- * EXPERIMENTAL: SIGNATURE MAY CHANGE IN FUTURE RELEASES - * - * @author Stephen Connolly - * @since 24-Apr-2008 22:12:35 - */ -public abstract class ComputerLauncher implements Describable, ExtensionPoint { - /** - * Returns true if this {@link ComputerLauncher} supports - * programatic launch of the slave agent in the target {@link Computer}. - */ - public boolean isLaunchSupported() { - return true; - } - - /** - * Launches the slave agent for the given {@link Computer}. - * - *

- * If the slave agent is launched successfully, {@link SlaveComputer#setChannel(InputStream, OutputStream, OutputStream, Listener)} - * should be invoked in the end to notify Hudson of the established connection. - * The operation could also fail, in which case there's no need to make any callback notification, - * (except to notify the user of the failure through {@link StreamTaskListener}.) - * - * @param listener - * The progress of the launch, as well as any error, should be sent to this listener. - */ - public abstract void launch(SlaveComputer computer, StreamTaskListener listener); - - /** - * Allows the {@link ComputerLauncher} to tidy-up after a disconnect. - */ - public void afterDisconnect(SlaveComputer computer, StreamTaskListener listener) { - } - - /** - * Allows the {@link ComputerLauncher} to prepare for a disconnect. - */ - public void beforeDisconnect(SlaveComputer computer, StreamTaskListener listener) { - } - - /** - * All registered {@link ComputerLauncher} implementations. - */ - public static final DescriptorList LIST = new DescriptorList(); - - static { - LIST.load(JNLPLauncher.class); - LIST.load(CommandLauncher.class); - } -} +package hudson.slaves; + +import hudson.ExtensionPoint; +import hudson.model.Computer; +import hudson.model.Describable; +import hudson.remoting.Channel.Listener; +import hudson.util.DescriptorList; +import hudson.util.StreamTaskListener; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Extension point to allow control over how {@link Computer}s are "launched", + * meaning how they get connected to their slave agent program. + * + *

+ * EXPERIMENTAL: SIGNATURE MAY CHANGE IN FUTURE RELEASES + * + * @author Stephen Connolly + * @since 24-Apr-2008 22:12:35 + */ +public abstract class ComputerLauncher implements Describable, ExtensionPoint { + /** + * Returns true if this {@link ComputerLauncher} supports + * programatic launch of the slave agent in the target {@link Computer}. + */ + public boolean isLaunchSupported() { + return true; + } + + /** + * Launches the slave agent for the given {@link Computer}. + * + *

+ * If the slave agent is launched successfully, {@link SlaveComputer#setChannel(InputStream, OutputStream, OutputStream, Listener)} + * should be invoked in the end to notify Hudson of the established connection. + * The operation could also fail, in which case there's no need to make any callback notification, + * (except to notify the user of the failure through {@link StreamTaskListener}.) + * + * @param listener + * The progress of the launch, as well as any error, should be sent to this listener. + */ + public abstract void launch(SlaveComputer computer, StreamTaskListener listener); + + /** + * Allows the {@link ComputerLauncher} to tidy-up after a disconnect. + */ + public void afterDisconnect(SlaveComputer computer, StreamTaskListener listener) { + } + + /** + * Allows the {@link ComputerLauncher} to prepare for a disconnect. + */ + public void beforeDisconnect(SlaveComputer computer, StreamTaskListener listener) { + } + + /** + * All registered {@link ComputerLauncher} implementations. + */ + public static final DescriptorList LIST = new DescriptorList(); + + static { + LIST.load(JNLPLauncher.class); + LIST.load(CommandLauncher.class); + } +} diff --git a/core/src/main/java/hudson/slaves/RetentionStrategy.java b/core/src/main/java/hudson/slaves/RetentionStrategy.java index 9fc3edb1b88699b470fde5155d5b9f3d0726522a..71d79ae20574b916fe4774ef0848d57c2d4e0bb3 100644 --- a/core/src/main/java/hudson/slaves/RetentionStrategy.java +++ b/core/src/main/java/hudson/slaves/RetentionStrategy.java @@ -1,206 +1,206 @@ -package hudson.slaves; - -import hudson.ExtensionPoint; -import hudson.Util; -import hudson.model.Computer; -import hudson.model.Describable; -import hudson.model.Descriptor; -import hudson.util.DescriptorList; -import org.kohsuke.stapler.DataBoundConstructor; - -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Controls when to take {@link Computer} offline, bring it back online, or even to destroy it. - *

- * EXPERIMENTAL: SIGNATURE MAY CHANGE IN FUTURE RELEASES - * - * @author Stephen Connolly - * @author Kohsuke Kawaguchi - */ -public abstract class RetentionStrategy implements Describable>, ExtensionPoint { - - /** - * This method will be called periodically to allow this strategy to decide what to do with it's owning slave. - * - * @param c {@link Computer} for which this strategy is assigned. This object also exposes a bunch of properties - * that the callee can use to decide what action to take. - * @return The number of minutes after which the strategy would like to be checked again. The strategy may be - * rechecked earlier or later that this! - */ - public abstract long check(T c); - - /** - * All registered {@link RetentionStrategy} implementations. - */ - public static final DescriptorList> LIST = new DescriptorList>(); - - /** - * Dummy instance that doesn't do any attempt to retention. - */ - public static final RetentionStrategy NOOP = new RetentionStrategy() { - public long check(Computer c) { - return 1; - } - - public Descriptor> getDescriptor() { - throw new UnsupportedOperationException(); - } - }; - - /** - * Convenient singleton instance, since this {@link RetentionStrategy} is stateless. - */ - public static final Always INSTANCE = new Always(); - - /** - * {@link RetentionStrategy} that tries to keep the node online all the time. - */ - public static class Always extends RetentionStrategy { - /** - * Constructs a new Always. - */ - @DataBoundConstructor - public Always() { - } - - /** - * {@inheritDoc} - */ - public long check(SlaveComputer c) { - if (c.isOffline() && c.isLaunchSupported()) - c.tryReconnect(); - return 1; - } - - /** - * {@inheritDoc} - */ - public DescriptorImpl getDescriptor() { - return DESCRIPTOR; - } - - public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); - - private static class DescriptorImpl extends Descriptor> { - /** - * Constructs a new DescriptorImpl. - */ - public DescriptorImpl() { - super(Always.class); - } - - /** - * {@inheritDoc} - */ - public String getDisplayName() { - return Messages.RetentionStrategy_Always_displayName(); - } - } - - static { - LIST.add(DESCRIPTOR); - } - } - - /** - * {@link hudson.slaves.RetentionStrategy} that tries to keep the node offline when not in use. - */ - public static class Demand extends RetentionStrategy { - - private static final Logger logger = Logger.getLogger(Demand.class.getName()); - - /** - * The delay (in minutes) for which the slave must be in demand before tring to launch it. - */ - private final long inDemandDelay; - - /** - * The delay (in minutes) for which the slave must be idle before taking it offline. - */ - private final long idleDelay; - - @DataBoundConstructor - public Demand(long inDemandDelay, long idleDelay) { - this.inDemandDelay = Math.max(1, inDemandDelay); - this.idleDelay = Math.max(1, idleDelay); - } - - /** - * Getter for property 'inDemandDelay'. - * - * @return Value for property 'inDemandDelay'. - */ - public long getInDemandDelay() { - return inDemandDelay; - } - - /** - * Getter for property 'idleDelay'. - * - * @return Value for property 'idleDelay'. - */ - public long getIdleDelay() { - return idleDelay; - } - - public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); - - /** - * {@inheritDoc} - */ - public synchronized long check(SlaveComputer c) { - if (c.isOffline()) { - final long demandMilliseconds = System.currentTimeMillis() - c.getDemandStartMilliseconds(); - if (demandMilliseconds > inDemandDelay * 1000 * 60 /*MINS->MILLIS*/) { - // we've been in demand for long enough - logger.log(Level.INFO, "Launching computer {0} as it has been in demand for {1}", - new Object[]{c.getNode().getNodeName(), Util.getTimeSpanString(demandMilliseconds)}); - if (c.isLaunchSupported()) - c.launch(); - } - } else if (c.isIdle()) { - final long idleMilliseconds = System.currentTimeMillis() - c.getIdleStartMilliseconds(); - if (idleMilliseconds > idleDelay * 1000 * 60 /*MINS->MILLIS*/) { - // we've been idle for long enough - logger.log(Level.INFO, "Disconnecting computer {0} as it has been idle for {1}", - new Object[]{c.getNode().getNodeName(), Util.getTimeSpanString(idleMilliseconds)}); - c.disconnect(); - } - } - return 1; - } - - /** - * {@inheritDoc} - */ - public Descriptor> getDescriptor() { - return DESCRIPTOR; - } - - private static class DescriptorImpl extends Descriptor> { - /** - * Constructs a new DescriptorImpl. - */ - public DescriptorImpl() { - super(Demand.class); - } - - /** - * {@inheritDoc} - */ - public String getDisplayName() { - return Messages.RetentionStrategy_Demand_displayName(); - } - } - - static { - LIST.add(DESCRIPTOR); - } - } - - static { - LIST.load(Demand.class); - } -} +package hudson.slaves; + +import hudson.ExtensionPoint; +import hudson.Util; +import hudson.model.Computer; +import hudson.model.Describable; +import hudson.model.Descriptor; +import hudson.util.DescriptorList; +import org.kohsuke.stapler.DataBoundConstructor; + +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Controls when to take {@link Computer} offline, bring it back online, or even to destroy it. + *

+ * EXPERIMENTAL: SIGNATURE MAY CHANGE IN FUTURE RELEASES + * + * @author Stephen Connolly + * @author Kohsuke Kawaguchi + */ +public abstract class RetentionStrategy implements Describable>, ExtensionPoint { + + /** + * This method will be called periodically to allow this strategy to decide what to do with it's owning slave. + * + * @param c {@link Computer} for which this strategy is assigned. This object also exposes a bunch of properties + * that the callee can use to decide what action to take. + * @return The number of minutes after which the strategy would like to be checked again. The strategy may be + * rechecked earlier or later that this! + */ + public abstract long check(T c); + + /** + * All registered {@link RetentionStrategy} implementations. + */ + public static final DescriptorList> LIST = new DescriptorList>(); + + /** + * Dummy instance that doesn't do any attempt to retention. + */ + public static final RetentionStrategy NOOP = new RetentionStrategy() { + public long check(Computer c) { + return 1; + } + + public Descriptor> getDescriptor() { + throw new UnsupportedOperationException(); + } + }; + + /** + * Convenient singleton instance, since this {@link RetentionStrategy} is stateless. + */ + public static final Always INSTANCE = new Always(); + + /** + * {@link RetentionStrategy} that tries to keep the node online all the time. + */ + public static class Always extends RetentionStrategy { + /** + * Constructs a new Always. + */ + @DataBoundConstructor + public Always() { + } + + /** + * {@inheritDoc} + */ + public long check(SlaveComputer c) { + if (c.isOffline() && c.isLaunchSupported()) + c.tryReconnect(); + return 1; + } + + /** + * {@inheritDoc} + */ + public DescriptorImpl getDescriptor() { + return DESCRIPTOR; + } + + public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); + + private static class DescriptorImpl extends Descriptor> { + /** + * Constructs a new DescriptorImpl. + */ + public DescriptorImpl() { + super(Always.class); + } + + /** + * {@inheritDoc} + */ + public String getDisplayName() { + return Messages.RetentionStrategy_Always_displayName(); + } + } + + static { + LIST.add(DESCRIPTOR); + } + } + + /** + * {@link hudson.slaves.RetentionStrategy} that tries to keep the node offline when not in use. + */ + public static class Demand extends RetentionStrategy { + + private static final Logger logger = Logger.getLogger(Demand.class.getName()); + + /** + * The delay (in minutes) for which the slave must be in demand before tring to launch it. + */ + private final long inDemandDelay; + + /** + * The delay (in minutes) for which the slave must be idle before taking it offline. + */ + private final long idleDelay; + + @DataBoundConstructor + public Demand(long inDemandDelay, long idleDelay) { + this.inDemandDelay = Math.max(1, inDemandDelay); + this.idleDelay = Math.max(1, idleDelay); + } + + /** + * Getter for property 'inDemandDelay'. + * + * @return Value for property 'inDemandDelay'. + */ + public long getInDemandDelay() { + return inDemandDelay; + } + + /** + * Getter for property 'idleDelay'. + * + * @return Value for property 'idleDelay'. + */ + public long getIdleDelay() { + return idleDelay; + } + + public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); + + /** + * {@inheritDoc} + */ + public synchronized long check(SlaveComputer c) { + if (c.isOffline()) { + final long demandMilliseconds = System.currentTimeMillis() - c.getDemandStartMilliseconds(); + if (demandMilliseconds > inDemandDelay * 1000 * 60 /*MINS->MILLIS*/) { + // we've been in demand for long enough + logger.log(Level.INFO, "Launching computer {0} as it has been in demand for {1}", + new Object[]{c.getNode().getNodeName(), Util.getTimeSpanString(demandMilliseconds)}); + if (c.isLaunchSupported()) + c.launch(); + } + } else if (c.isIdle()) { + final long idleMilliseconds = System.currentTimeMillis() - c.getIdleStartMilliseconds(); + if (idleMilliseconds > idleDelay * 1000 * 60 /*MINS->MILLIS*/) { + // we've been idle for long enough + logger.log(Level.INFO, "Disconnecting computer {0} as it has been idle for {1}", + new Object[]{c.getNode().getNodeName(), Util.getTimeSpanString(idleMilliseconds)}); + c.disconnect(); + } + } + return 1; + } + + /** + * {@inheritDoc} + */ + public Descriptor> getDescriptor() { + return DESCRIPTOR; + } + + private static class DescriptorImpl extends Descriptor> { + /** + * Constructs a new DescriptorImpl. + */ + public DescriptorImpl() { + super(Demand.class); + } + + /** + * {@inheritDoc} + */ + public String getDisplayName() { + return Messages.RetentionStrategy_Demand_displayName(); + } + } + + static { + LIST.add(DESCRIPTOR); + } + } + + static { + LIST.load(Demand.class); + } +} diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java index bdb1a9cade631dcec2aebab92419f24b462dc4a0..f3f5220fea14872f6722220c12dbff5391239a23 100644 --- a/core/src/main/java/hudson/slaves/SlaveComputer.java +++ b/core/src/main/java/hudson/slaves/SlaveComputer.java @@ -1,378 +1,378 @@ -package hudson.slaves; - -import hudson.model.*; -import hudson.remoting.Channel; -import hudson.remoting.Which; -import hudson.remoting.VirtualChannel; -import hudson.remoting.Callable; -import hudson.util.StreamTaskListener; -import hudson.util.NullStream; -import hudson.util.RingBufferLogHandler; -import hudson.FilePath; -import hudson.lifecycle.WindowsSlaveInstaller; -import hudson.maven.agent.Main; -import hudson.maven.agent.PluginManagerInterceptor; - -import java.io.File; -import java.io.OutputStream; -import java.io.FileOutputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import java.util.logging.Logger; -import java.util.List; -import java.util.Collections; -import java.util.ArrayList; -import java.nio.charset.Charset; - -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletResponse; - -/** - * {@link Computer} for {@link Slave}s. - * - * @author Kohsuke Kawaguchi - */ -public final class SlaveComputer extends Computer { - private volatile Channel channel; - private volatile transient boolean acceptingTasks = true; - private Charset defaultCharset; - private Boolean isUnix; - private ComputerLauncher launcher; - - /** - * Number of failed attempts to reconnect to this node - * (so that if we keep failing to reconnect, we can stop - * trying.) - */ - private transient int numRetryAttempt; - - - /** - * {@inheritDoc} - */ - @Override - public boolean isAcceptingTasks() { - return acceptingTasks; - } - - /** - * Allows a {@linkplain hudson.slaves.ComputerLauncher} or a {@linkplain hudson.slaves.RetentionStrategy} to - * suspend tasks being accepted by the slave computer. - * - * @param acceptingTasks {@code true} if the slave can accept tasks. - */ - public void setAcceptingTasks(boolean acceptingTasks) { - this.acceptingTasks = acceptingTasks; - } - - public SlaveComputer(Slave slave) { - super(slave); - } - - /** - * True if this computer is a Unix machine (as opposed to Windows machine). - * - * @return - * null if the computer is disconnected and therefore we don't know whether it is Unix or not. - */ - public Boolean isUnix() { - return isUnix; - } - - public Slave getNode() { - return (Slave)super.getNode(); - } - - @Override - @Deprecated - public boolean isJnlpAgent() { - return launcher instanceof JNLPLauncher; - } - - @Override - public boolean isLaunchSupported() { - return launcher.isLaunchSupported(); - } - - public ComputerLauncher getLauncher() { - return launcher; - } - - public void launch() { - if(channel!=null) return; - - closeChannel(); - Computer.threadPoolForRemoting.execute(new Runnable() { - public void run() { - // do this on another thread so that the lengthy launch operation - // (which is typical) won't block UI thread. - launcher.launch(SlaveComputer.this, new StreamTaskListener(openLogFile())); - } - }); - } - - /** - * {@inheritDoc} - */ - @Override - public void taskAccepted(Executor executor, Queue.Task task) { - super.taskAccepted(executor, task); - if (launcher instanceof ExecutorListener) { - ((ExecutorListener)launcher).taskAccepted(executor, task); - } - if (getNode().getRetentionStrategy() instanceof ExecutorListener) { - ((ExecutorListener)getNode().getRetentionStrategy()).taskAccepted(executor, task); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void taskCompleted(Executor executor, Queue.Task task, long durationMS) { - super.taskCompleted(executor, task, durationMS); - if (launcher instanceof ExecutorListener) { - ((ExecutorListener)launcher).taskCompleted(executor, task, durationMS); - } - if (getNode().getRetentionStrategy() instanceof ExecutorListener) { - ((ExecutorListener)getNode().getRetentionStrategy()).taskCompleted(executor, task, durationMS); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void taskCompletedWithProblems(Executor executor, Queue.Task task, long durationMS, Throwable problems) { - super.taskCompletedWithProblems(executor, task, durationMS, problems); - if (launcher instanceof ExecutorListener) { - ((ExecutorListener)launcher).taskCompletedWithProblems(executor, task, durationMS, problems); - } - if (getNode().getRetentionStrategy() instanceof ExecutorListener) { - ((ExecutorListener)getNode().getRetentionStrategy()).taskCompletedWithProblems(executor, task, durationMS, - problems); - } - } - - public OutputStream openLogFile() { - OutputStream os; - try { - os = new FileOutputStream(getLogFile()); - } catch (FileNotFoundException e) { - logger.log(Level.SEVERE, "Failed to create log file "+getLogFile(),e); - os = new NullStream(); - } - return os; - } - - private final Object channelLock = new Object(); - - /** - * Creates a {@link Channel} from the given stream and sets that to this slave. - */ - public void setChannel(InputStream in, OutputStream out, OutputStream launchLog, Channel.Listener listener) throws IOException, InterruptedException { - if(this.channel!=null) - throw new IllegalStateException("Already connected"); - - Channel channel = new Channel(nodeName,threadPoolForRemoting, Channel.Mode.NEGOTIATE, - in,out, launchLog); - channel.addListener(new Channel.Listener() { - public void onClosed(Channel c,IOException cause) { - SlaveComputer.this.channel = null; - } - }); - channel.addListener(listener); - - PrintWriter log = new PrintWriter(launchLog,true); - - boolean _isUnix = channel.call(new DetectOS()); - log.println(_isUnix? hudson.model.Messages.Slave_UnixSlave():hudson.model.Messages.Slave_WindowsSlave()); - - String defaultCharsetName = channel.call(new DetectDefaultCharset()); - - String remoteFs = getNode().getRemoteFS(); - if(_isUnix && !remoteFs.contains("/") && remoteFs.contains("\\")) - log.println("WARNING: "+remoteFs+" looks suspiciously like Windows path. Maybe you meant "+remoteFs.replace('\\','/')+"?"); - - {// send jars that we need for our operations - // TODO: maybe I should generalize this kind of "post initialization" processing - FilePath dst = new FilePath(channel, remoteFs); - new FilePath(Which.jarFile(Main.class)).copyTo(dst.child("maven-agent.jar")); - log.println("Copied maven-agent.jar"); - new FilePath(Which.jarFile(PluginManagerInterceptor.class)).copyTo(dst.child("maven-interceptor.jar")); - log.println("Copied maven-interceptor.jar"); - } - - channel.call(new LogInstaller()); - channel.call(new WindowsSlaveInstaller(remoteFs)); - - // update the data structure atomically to prevent others from seeing a channel that's not properly initialized yet - synchronized(channelLock) { - if(this.channel!=null) { - // check again. we used to have this entire method in a big sycnhronization block, - // but Channel constructor blocks for an external process to do the connection - // if CommandLauncher is used, and that cannot be interrupted because it blocks at InputStream. - // so if the process hangs, it hangs the thread in a lock, and since Hudson will try to relaunch, - // we'll end up queuing the lot of threads in a pseudo deadlock. - // This implementation prevents that by avoiding a lock. HUDSON-1705 is likely a manifestation of this. - channel.close(); - throw new IllegalStateException("Already connected"); - } - isUnix = _isUnix; - numRetryAttempt = 0; - this.channel = channel; - defaultCharset = Charset.forName(defaultCharsetName); - } - for (ComputerListener cl : Hudson.getInstance().getComputerListeners()) - cl.onOnline(this); - Hudson.getInstance().getQueue().scheduleMaintenance(); - } - - @Override - public VirtualChannel getChannel() { - return channel; - } - - public Charset getDefaultCharset() { - return defaultCharset; - } - - public List getLogRecords() throws IOException, InterruptedException { - if(channel==null) - return Collections.emptyList(); - else - return channel.call(new Callable,RuntimeException>() { - public List call() { - return new ArrayList(SLAVE_LOG_HANDLER.getView()); - } - }); - } - - public void doDoDisconnect(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - checkPermission(Hudson.ADMINISTER); - disconnect(); - rsp.sendRedirect("."); - } - - @Override - public void disconnect() { - Computer.threadPoolForRemoting.execute(new Runnable() { - public void run() { - // do this on another thread so that any lengthy disconnect operation - // (which could be typical) won't block UI thread. - StreamTaskListener listener = new StreamTaskListener(openLogFile()); - launcher.beforeDisconnect(SlaveComputer.this, listener); - closeChannel(); - launcher.afterDisconnect(SlaveComputer.this, listener); - } - }); - } - - public void doLaunchSlaveAgent(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - if(channel!=null) { - rsp.sendError(HttpServletResponse.SC_NOT_FOUND); - return; - } - - launch(); - - // TODO: would be nice to redirect the user to "launching..." wait page, - // then spend a few seconds there and poll for the completion periodically. - rsp.sendRedirect("log"); - } - - public void tryReconnect() { - numRetryAttempt++; - if(numRetryAttempt<6 || (numRetryAttempt%12)==0) { - // initially retry several times quickly, and after that, do it infrequently. - logger.info("Attempting to reconnect "+nodeName); - launch(); - } - } - - /** - * Serves jar files for JNLP slave agents. - * - * @deprecated - * This URL binding is no longer used and moved up directly under to {@link Hudson}, - * but it's left here for now just in case some old JNLP slave agents request it. - */ - public Slave.JnlpJar getJnlpJars(String fileName) { - return new Slave.JnlpJar(fileName); - } - - @Override - protected void kill() { - super.kill(); - closeChannel(); - } - - public RetentionStrategy getRetentionStrategy() { - return getNode().getRetentionStrategy(); - } - - /** - * If still connected, disconnect. - */ - private void closeChannel() { - // TODO: race condition between this and the setChannel method. - Channel c = channel; - channel = null; - isUnix = null; - if (c != null) { - try { - c.close(); - } catch (IOException e) { - logger.log(Level.SEVERE, "Failed to terminate channel to " + getDisplayName(), e); - } - } - for (ComputerListener cl : Hudson.getInstance().getComputerListeners()) - cl.onOffline(this); - } - - @Override - protected void setNode(Node node) { - super.setNode(node); - launcher = ((Slave)node).getLauncher(); - - // maybe the configuration was changed to relaunch the slave, so try to re-launch now. - launch(); - } - - private static final Logger logger = Logger.getLogger(SlaveComputer.class.getName()); - - private static final class DetectOS implements Callable { - public Boolean call() throws IOException { - return File.pathSeparatorChar==':'; - } - } - - private static final class DetectDefaultCharset implements Callable { - public String call() throws IOException { - return Charset.defaultCharset().name(); - } - } - - /** - * This field is used on each slave node to record log records on the slave. - */ - private static final RingBufferLogHandler SLAVE_LOG_HANDLER = new RingBufferLogHandler(); - - private static class LogInstaller implements Callable { - public Void call() { - // avoid double installation of the handler - Logger logger = Logger.getLogger("hudson"); - logger.removeHandler(SLAVE_LOG_HANDLER); - logger.addHandler(SLAVE_LOG_HANDLER); - return null; - } - private static final long serialVersionUID = 1L; - } -} +package hudson.slaves; + +import hudson.model.*; +import hudson.remoting.Channel; +import hudson.remoting.Which; +import hudson.remoting.VirtualChannel; +import hudson.remoting.Callable; +import hudson.util.StreamTaskListener; +import hudson.util.NullStream; +import hudson.util.RingBufferLogHandler; +import hudson.FilePath; +import hudson.lifecycle.WindowsSlaveInstaller; +import hudson.maven.agent.Main; +import hudson.maven.agent.PluginManagerInterceptor; + +import java.io.File; +import java.io.OutputStream; +import java.io.FileOutputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import java.util.List; +import java.util.Collections; +import java.util.ArrayList; +import java.nio.charset.Charset; + +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletResponse; + +/** + * {@link Computer} for {@link Slave}s. + * + * @author Kohsuke Kawaguchi + */ +public final class SlaveComputer extends Computer { + private volatile Channel channel; + private volatile transient boolean acceptingTasks = true; + private Charset defaultCharset; + private Boolean isUnix; + private ComputerLauncher launcher; + + /** + * Number of failed attempts to reconnect to this node + * (so that if we keep failing to reconnect, we can stop + * trying.) + */ + private transient int numRetryAttempt; + + + /** + * {@inheritDoc} + */ + @Override + public boolean isAcceptingTasks() { + return acceptingTasks; + } + + /** + * Allows a {@linkplain hudson.slaves.ComputerLauncher} or a {@linkplain hudson.slaves.RetentionStrategy} to + * suspend tasks being accepted by the slave computer. + * + * @param acceptingTasks {@code true} if the slave can accept tasks. + */ + public void setAcceptingTasks(boolean acceptingTasks) { + this.acceptingTasks = acceptingTasks; + } + + public SlaveComputer(Slave slave) { + super(slave); + } + + /** + * True if this computer is a Unix machine (as opposed to Windows machine). + * + * @return + * null if the computer is disconnected and therefore we don't know whether it is Unix or not. + */ + public Boolean isUnix() { + return isUnix; + } + + public Slave getNode() { + return (Slave)super.getNode(); + } + + @Override + @Deprecated + public boolean isJnlpAgent() { + return launcher instanceof JNLPLauncher; + } + + @Override + public boolean isLaunchSupported() { + return launcher.isLaunchSupported(); + } + + public ComputerLauncher getLauncher() { + return launcher; + } + + public void launch() { + if(channel!=null) return; + + closeChannel(); + Computer.threadPoolForRemoting.execute(new Runnable() { + public void run() { + // do this on another thread so that the lengthy launch operation + // (which is typical) won't block UI thread. + launcher.launch(SlaveComputer.this, new StreamTaskListener(openLogFile())); + } + }); + } + + /** + * {@inheritDoc} + */ + @Override + public void taskAccepted(Executor executor, Queue.Task task) { + super.taskAccepted(executor, task); + if (launcher instanceof ExecutorListener) { + ((ExecutorListener)launcher).taskAccepted(executor, task); + } + if (getNode().getRetentionStrategy() instanceof ExecutorListener) { + ((ExecutorListener)getNode().getRetentionStrategy()).taskAccepted(executor, task); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void taskCompleted(Executor executor, Queue.Task task, long durationMS) { + super.taskCompleted(executor, task, durationMS); + if (launcher instanceof ExecutorListener) { + ((ExecutorListener)launcher).taskCompleted(executor, task, durationMS); + } + if (getNode().getRetentionStrategy() instanceof ExecutorListener) { + ((ExecutorListener)getNode().getRetentionStrategy()).taskCompleted(executor, task, durationMS); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void taskCompletedWithProblems(Executor executor, Queue.Task task, long durationMS, Throwable problems) { + super.taskCompletedWithProblems(executor, task, durationMS, problems); + if (launcher instanceof ExecutorListener) { + ((ExecutorListener)launcher).taskCompletedWithProblems(executor, task, durationMS, problems); + } + if (getNode().getRetentionStrategy() instanceof ExecutorListener) { + ((ExecutorListener)getNode().getRetentionStrategy()).taskCompletedWithProblems(executor, task, durationMS, + problems); + } + } + + public OutputStream openLogFile() { + OutputStream os; + try { + os = new FileOutputStream(getLogFile()); + } catch (FileNotFoundException e) { + logger.log(Level.SEVERE, "Failed to create log file "+getLogFile(),e); + os = new NullStream(); + } + return os; + } + + private final Object channelLock = new Object(); + + /** + * Creates a {@link Channel} from the given stream and sets that to this slave. + */ + public void setChannel(InputStream in, OutputStream out, OutputStream launchLog, Channel.Listener listener) throws IOException, InterruptedException { + if(this.channel!=null) + throw new IllegalStateException("Already connected"); + + Channel channel = new Channel(nodeName,threadPoolForRemoting, Channel.Mode.NEGOTIATE, + in,out, launchLog); + channel.addListener(new Channel.Listener() { + public void onClosed(Channel c,IOException cause) { + SlaveComputer.this.channel = null; + } + }); + channel.addListener(listener); + + PrintWriter log = new PrintWriter(launchLog,true); + + boolean _isUnix = channel.call(new DetectOS()); + log.println(_isUnix? hudson.model.Messages.Slave_UnixSlave():hudson.model.Messages.Slave_WindowsSlave()); + + String defaultCharsetName = channel.call(new DetectDefaultCharset()); + + String remoteFs = getNode().getRemoteFS(); + if(_isUnix && !remoteFs.contains("/") && remoteFs.contains("\\")) + log.println("WARNING: "+remoteFs+" looks suspiciously like Windows path. Maybe you meant "+remoteFs.replace('\\','/')+"?"); + + {// send jars that we need for our operations + // TODO: maybe I should generalize this kind of "post initialization" processing + FilePath dst = new FilePath(channel, remoteFs); + new FilePath(Which.jarFile(Main.class)).copyTo(dst.child("maven-agent.jar")); + log.println("Copied maven-agent.jar"); + new FilePath(Which.jarFile(PluginManagerInterceptor.class)).copyTo(dst.child("maven-interceptor.jar")); + log.println("Copied maven-interceptor.jar"); + } + + channel.call(new LogInstaller()); + channel.call(new WindowsSlaveInstaller(remoteFs)); + + // update the data structure atomically to prevent others from seeing a channel that's not properly initialized yet + synchronized(channelLock) { + if(this.channel!=null) { + // check again. we used to have this entire method in a big sycnhronization block, + // but Channel constructor blocks for an external process to do the connection + // if CommandLauncher is used, and that cannot be interrupted because it blocks at InputStream. + // so if the process hangs, it hangs the thread in a lock, and since Hudson will try to relaunch, + // we'll end up queuing the lot of threads in a pseudo deadlock. + // This implementation prevents that by avoiding a lock. HUDSON-1705 is likely a manifestation of this. + channel.close(); + throw new IllegalStateException("Already connected"); + } + isUnix = _isUnix; + numRetryAttempt = 0; + this.channel = channel; + defaultCharset = Charset.forName(defaultCharsetName); + } + for (ComputerListener cl : Hudson.getInstance().getComputerListeners()) + cl.onOnline(this); + Hudson.getInstance().getQueue().scheduleMaintenance(); + } + + @Override + public VirtualChannel getChannel() { + return channel; + } + + public Charset getDefaultCharset() { + return defaultCharset; + } + + public List getLogRecords() throws IOException, InterruptedException { + if(channel==null) + return Collections.emptyList(); + else + return channel.call(new Callable,RuntimeException>() { + public List call() { + return new ArrayList(SLAVE_LOG_HANDLER.getView()); + } + }); + } + + public void doDoDisconnect(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { + checkPermission(Hudson.ADMINISTER); + disconnect(); + rsp.sendRedirect("."); + } + + @Override + public void disconnect() { + Computer.threadPoolForRemoting.execute(new Runnable() { + public void run() { + // do this on another thread so that any lengthy disconnect operation + // (which could be typical) won't block UI thread. + StreamTaskListener listener = new StreamTaskListener(openLogFile()); + launcher.beforeDisconnect(SlaveComputer.this, listener); + closeChannel(); + launcher.afterDisconnect(SlaveComputer.this, listener); + } + }); + } + + public void doLaunchSlaveAgent(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { + if(channel!=null) { + rsp.sendError(HttpServletResponse.SC_NOT_FOUND); + return; + } + + launch(); + + // TODO: would be nice to redirect the user to "launching..." wait page, + // then spend a few seconds there and poll for the completion periodically. + rsp.sendRedirect("log"); + } + + public void tryReconnect() { + numRetryAttempt++; + if(numRetryAttempt<6 || (numRetryAttempt%12)==0) { + // initially retry several times quickly, and after that, do it infrequently. + logger.info("Attempting to reconnect "+nodeName); + launch(); + } + } + + /** + * Serves jar files for JNLP slave agents. + * + * @deprecated + * This URL binding is no longer used and moved up directly under to {@link Hudson}, + * but it's left here for now just in case some old JNLP slave agents request it. + */ + public Slave.JnlpJar getJnlpJars(String fileName) { + return new Slave.JnlpJar(fileName); + } + + @Override + protected void kill() { + super.kill(); + closeChannel(); + } + + public RetentionStrategy getRetentionStrategy() { + return getNode().getRetentionStrategy(); + } + + /** + * If still connected, disconnect. + */ + private void closeChannel() { + // TODO: race condition between this and the setChannel method. + Channel c = channel; + channel = null; + isUnix = null; + if (c != null) { + try { + c.close(); + } catch (IOException e) { + logger.log(Level.SEVERE, "Failed to terminate channel to " + getDisplayName(), e); + } + } + for (ComputerListener cl : Hudson.getInstance().getComputerListeners()) + cl.onOffline(this); + } + + @Override + protected void setNode(Node node) { + super.setNode(node); + launcher = ((Slave)node).getLauncher(); + + // maybe the configuration was changed to relaunch the slave, so try to re-launch now. + launch(); + } + + private static final Logger logger = Logger.getLogger(SlaveComputer.class.getName()); + + private static final class DetectOS implements Callable { + public Boolean call() throws IOException { + return File.pathSeparatorChar==':'; + } + } + + private static final class DetectDefaultCharset implements Callable { + public String call() throws IOException { + return Charset.defaultCharset().name(); + } + } + + /** + * This field is used on each slave node to record log records on the slave. + */ + private static final RingBufferLogHandler SLAVE_LOG_HANDLER = new RingBufferLogHandler(); + + private static class LogInstaller implements Callable { + public Void call() { + // avoid double installation of the handler + Logger logger = Logger.getLogger("hudson"); + logger.removeHandler(SLAVE_LOG_HANDLER); + logger.addHandler(SLAVE_LOG_HANDLER); + return null; + } + private static final long serialVersionUID = 1L; + } +} diff --git a/core/src/main/java/hudson/tasks/UserNameResolver.java b/core/src/main/java/hudson/tasks/UserNameResolver.java index 8e9b038e9c153f6538497c07f530eb552338cb71..41499bbe001bce0f9a97736139be2f9ec8aa7c11 100644 --- a/core/src/main/java/hudson/tasks/UserNameResolver.java +++ b/core/src/main/java/hudson/tasks/UserNameResolver.java @@ -1,72 +1,72 @@ -package hudson.tasks; - -import hudson.ExtensionPoint; -import hudson.Plugin; -import hudson.scm.SCM; -import hudson.scm.CVSSCM; -import hudson.scm.SubversionSCM; -import hudson.model.User; -import hudson.model.AbstractProject; - -import java.util.List; -import java.util.Map; -import java.util.HashMap; -import java.util.regex.Pattern; -import java.util.regex.Matcher; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * Finds full name off the user when none is specified. - * - *

- * This is an extension point of Hudson. Plugins tha contribute new implementation - * of this class must register it to {@link #LIST}. The typical way to do this is: - * - *

- * class PluginImpl extends {@link Plugin} {
- *   public void start() {
- *     ...
- *     UserNameResolver.LIST.add(new UserNameResolver());
- *   }
- * }
- * 
- * - * @author Kohsuke Kawaguchi - * @since 1.192 - */ -public abstract class UserNameResolver implements ExtensionPoint { - - /** - * Finds full name of the given user. - * - *

- * This method is called when a {@link User} without explicitly name is used. - * - *

- * When multiple resolvers are installed, they are consulted in order and - * the search will be over when a name is found by someoene. - * - *

- * Since {@link UserNameResolver} is singleton, this method can be invoked concurrently - * from multiple threads. - * - * @return - * null if the inference failed. - */ - public abstract String findNameFor(User u); - - public static String resolve(User u) { - for (UserNameResolver r : LIST) { - String name = r.findNameFor(u); - if(name!=null) return name; - } - - return null; - } - - /** - * All registered {@link UserNameResolver} implementations. - */ - public static final List LIST = new CopyOnWriteArrayList(); - -} +package hudson.tasks; + +import hudson.ExtensionPoint; +import hudson.Plugin; +import hudson.scm.SCM; +import hudson.scm.CVSSCM; +import hudson.scm.SubversionSCM; +import hudson.model.User; +import hudson.model.AbstractProject; + +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Finds full name off the user when none is specified. + * + *

+ * This is an extension point of Hudson. Plugins tha contribute new implementation + * of this class must register it to {@link #LIST}. The typical way to do this is: + * + *

+ * class PluginImpl extends {@link Plugin} {
+ *   public void start() {
+ *     ...
+ *     UserNameResolver.LIST.add(new UserNameResolver());
+ *   }
+ * }
+ * 
+ * + * @author Kohsuke Kawaguchi + * @since 1.192 + */ +public abstract class UserNameResolver implements ExtensionPoint { + + /** + * Finds full name of the given user. + * + *

+ * This method is called when a {@link User} without explicitly name is used. + * + *

+ * When multiple resolvers are installed, they are consulted in order and + * the search will be over when a name is found by someoene. + * + *

+ * Since {@link UserNameResolver} is singleton, this method can be invoked concurrently + * from multiple threads. + * + * @return + * null if the inference failed. + */ + public abstract String findNameFor(User u); + + public static String resolve(User u) { + for (UserNameResolver r : LIST) { + String name = r.findNameFor(u); + if(name!=null) return name; + } + + return null; + } + + /** + * All registered {@link UserNameResolver} implementations. + */ + public static final List LIST = new CopyOnWriteArrayList(); + +} diff --git a/core/src/main/java/hudson/util/PluginServletFilter.java b/core/src/main/java/hudson/util/PluginServletFilter.java index 67b88a72a1ba0d2981618a9dbbd208f9f4dbd2b2..5c12e00711b9a8986be2c62836b62963b36e6bd2 100644 --- a/core/src/main/java/hudson/util/PluginServletFilter.java +++ b/core/src/main/java/hudson/util/PluginServletFilter.java @@ -1,76 +1,76 @@ -package hudson.util; - -import hudson.ExtensionPoint; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.ArrayList; -import java.util.Vector; - -/** - * Servlet {@link Filter} that chains multiple {@link Filter}s, provided by plugins - * - *

- * While this class by itself is not an extension point, I'm marking this class - * as an extension point so that this class will be more discoverable. - */ -public class PluginServletFilter implements Filter, ExtensionPoint { - - private static final List LIST = new Vector(); - - private static FilterConfig filterConfig; - - public PluginServletFilter() { - } - - public void init(FilterConfig filterConfig) throws ServletException { - PluginServletFilter.filterConfig = filterConfig; - synchronized (LIST) { - for (Filter f : LIST) { - f.init(filterConfig); - } - } - } - - public static void addFilter(Filter filter) throws ServletException { - synchronized (LIST) { - if (filterConfig != null) { - filter.init(filterConfig); - } - LIST.add(filter); - } - } - - public void doFilter(ServletRequest request, ServletResponse response, final FilterChain chain) throws IOException, ServletException { - new FilterChain() { - private int position=0; - // capture the array for thread-safety - private final Filter[] filters = LIST.toArray(new Filter[LIST.size()]); - - public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { - if(position==filters.length) { - // reached to the end - chain.doFilter(request,response); - } else { - // call next - filters[position++].doFilter(request,response,this); - } - } - }.doFilter(request,response); - } - - public void destroy() { - synchronized (LIST) { - for (Filter f : LIST) - f.destroy(); - } - } -} +package hudson.util; + +import hudson.ExtensionPoint; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.ArrayList; +import java.util.Vector; + +/** + * Servlet {@link Filter} that chains multiple {@link Filter}s, provided by plugins + * + *

+ * While this class by itself is not an extension point, I'm marking this class + * as an extension point so that this class will be more discoverable. + */ +public class PluginServletFilter implements Filter, ExtensionPoint { + + private static final List LIST = new Vector(); + + private static FilterConfig filterConfig; + + public PluginServletFilter() { + } + + public void init(FilterConfig filterConfig) throws ServletException { + PluginServletFilter.filterConfig = filterConfig; + synchronized (LIST) { + for (Filter f : LIST) { + f.init(filterConfig); + } + } + } + + public static void addFilter(Filter filter) throws ServletException { + synchronized (LIST) { + if (filterConfig != null) { + filter.init(filterConfig); + } + LIST.add(filter); + } + } + + public void doFilter(ServletRequest request, ServletResponse response, final FilterChain chain) throws IOException, ServletException { + new FilterChain() { + private int position=0; + // capture the array for thread-safety + private final Filter[] filters = LIST.toArray(new Filter[LIST.size()]); + + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { + if(position==filters.length) { + // reached to the end + chain.doFilter(request,response); + } else { + // call next + filters[position++].doFilter(request,response,this); + } + } + }.doFilter(request,response); + } + + public void destroy() { + synchronized (LIST) { + for (Filter f : LIST) + f.destroy(); + } + } +} diff --git a/core/src/main/java/hudson/util/TimeUnit2.java b/core/src/main/java/hudson/util/TimeUnit2.java index fdbb37d5125753a8c62a177e7e76026b1a091589..60e4f3d27fb6a85d67fd0aac2510e5539c26cf13 100644 --- a/core/src/main/java/hudson/util/TimeUnit2.java +++ b/core/src/main/java/hudson/util/TimeUnit2.java @@ -1,363 +1,363 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain - */ - -package hudson.util; - -import java.util.concurrent.TimeUnit; - -/** - * A TimeUnit represents time durations at a given unit of - * granularity and provides utility methods to convert across units, - * and to perform timing and delay operations in these units. A - * TimeUnit does not maintain time information, but only - * helps organize and use time representations that may be maintained - * separately across various contexts. A nanosecond is defined as one - * thousandth of a microsecond, a microsecond as one thousandth of a - * millisecond, a millisecond as one thousandth of a second, a minute - * as sixty seconds, an hour as sixty minutes, and a day as twenty four - * hours. - * - *

A TimeUnit is mainly used to inform time-based methods - * how a given timing parameter should be interpreted. For example, - * the following code will timeout in 50 milliseconds if the {@link - * java.util.concurrent.locks.Lock lock} is not available: - * - *

  Lock lock = ...;
- *  if ( lock.tryLock(50L, TimeUnit.MILLISECONDS) ) ...
- * 
- * while this code will timeout in 50 seconds: - *
- *  Lock lock = ...;
- *  if ( lock.tryLock(50L, TimeUnit.SECONDS) ) ...
- * 
- * - * Note however, that there is no guarantee that a particular timeout - * implementation will be able to notice the passage of time at the - * same granularity as the given TimeUnit. - * - * @since 1.5 - * @author Doug Lea - */ -public enum TimeUnit2 { - NANOSECONDS { - public long toNanos(long d) { return d; } - public long toMicros(long d) { return d/(C1/C0); } - public long toMillis(long d) { return d/(C2/C0); } - public long toSeconds(long d) { return d/(C3/C0); } - public long toMinutes(long d) { return d/(C4/C0); } - public long toHours(long d) { return d/(C5/C0); } - public long toDays(long d) { return d/(C6/C0); } - public long convert(long d, TimeUnit2 u) { return u.toNanos(d); } - public long convert(long d, TimeUnit u) { return u.toNanos(d); } - int excessNanos(long d, long m) { return (int)(d - (m*C2)); } - }, - MICROSECONDS { - public long toNanos(long d) { return x(d, C1/C0, MAX/(C1/C0)); } - public long toMicros(long d) { return d; } - public long toMillis(long d) { return d/(C2/C1); } - public long toSeconds(long d) { return d/(C3/C1); } - public long toMinutes(long d) { return d/(C4/C1); } - public long toHours(long d) { return d/(C5/C1); } - public long toDays(long d) { return d/(C6/C1); } - public long convert(long d, TimeUnit2 u) { return u.toMicros(d); } - public long convert(long d, TimeUnit u) { return u.toMicros(d); } - int excessNanos(long d, long m) { return (int)((d*C1) - (m*C2)); } - }, - MILLISECONDS { - public long toNanos(long d) { return x(d, C2/C0, MAX/(C2/C0)); } - public long toMicros(long d) { return x(d, C2/C1, MAX/(C2/C1)); } - public long toMillis(long d) { return d; } - public long toSeconds(long d) { return d/(C3/C2); } - public long toMinutes(long d) { return d/(C4/C2); } - public long toHours(long d) { return d/(C5/C2); } - public long toDays(long d) { return d/(C6/C2); } - public long convert(long d, TimeUnit2 u) { return u.toMillis(d); } - public long convert(long d, TimeUnit u) { return u.toMillis(d); } - int excessNanos(long d, long m) { return 0; } - }, - SECONDS { - public long toNanos(long d) { return x(d, C3/C0, MAX/(C3/C0)); } - public long toMicros(long d) { return x(d, C3/C1, MAX/(C3/C1)); } - public long toMillis(long d) { return x(d, C3/C2, MAX/(C3/C2)); } - public long toSeconds(long d) { return d; } - public long toMinutes(long d) { return d/(C4/C3); } - public long toHours(long d) { return d/(C5/C3); } - public long toDays(long d) { return d/(C6/C3); } - public long convert(long d, TimeUnit2 u) { return u.toSeconds(d); } - public long convert(long d, TimeUnit u) { return u.toSeconds(d); } - int excessNanos(long d, long m) { return 0; } - }, - MINUTES { - public long toNanos(long d) { return x(d, C4/C0, MAX/(C4/C0)); } - public long toMicros(long d) { return x(d, C4/C1, MAX/(C4/C1)); } - public long toMillis(long d) { return x(d, C4/C2, MAX/(C4/C2)); } - public long toSeconds(long d) { return x(d, C4/C3, MAX/(C4/C3)); } - public long toMinutes(long d) { return d; } - public long toHours(long d) { return d/(C5/C4); } - public long toDays(long d) { return d/(C6/C4); } - public long convert(long d, TimeUnit2 u) { return u.toMinutes(d); } - public long convert(long d, TimeUnit u) { return SECONDS.toMinutes(u.toSeconds(d)); } - int excessNanos(long d, long m) { return 0; } - }, - HOURS { - public long toNanos(long d) { return x(d, C5/C0, MAX/(C5/C0)); } - public long toMicros(long d) { return x(d, C5/C1, MAX/(C5/C1)); } - public long toMillis(long d) { return x(d, C5/C2, MAX/(C5/C2)); } - public long toSeconds(long d) { return x(d, C5/C3, MAX/(C5/C3)); } - public long toMinutes(long d) { return x(d, C5/C4, MAX/(C5/C4)); } - public long toHours(long d) { return d; } - public long toDays(long d) { return d/(C6/C5); } - public long convert(long d, TimeUnit2 u) { return u.toHours(d); } - public long convert(long d, TimeUnit u) { return SECONDS.toHours(u.toSeconds(d)); } - int excessNanos(long d, long m) { return 0; } - }, - DAYS { - public long toNanos(long d) { return x(d, C6/C0, MAX/(C6/C0)); } - public long toMicros(long d) { return x(d, C6/C1, MAX/(C6/C1)); } - public long toMillis(long d) { return x(d, C6/C2, MAX/(C6/C2)); } - public long toSeconds(long d) { return x(d, C6/C3, MAX/(C6/C3)); } - public long toMinutes(long d) { return x(d, C6/C4, MAX/(C6/C4)); } - public long toHours(long d) { return x(d, C6/C5, MAX/(C6/C5)); } - public long toDays(long d) { return d; } - public long convert(long d, TimeUnit2 u) { return u.toDays(d); } - public long convert(long d, TimeUnit u) { return SECONDS.toDays(u.toSeconds(d)); } - int excessNanos(long d, long m) { return 0; } - }; - - // Handy constants for conversion methods - static final long C0 = 1L; - static final long C1 = C0 * 1000L; - static final long C2 = C1 * 1000L; - static final long C3 = C2 * 1000L; - static final long C4 = C3 * 60L; - static final long C5 = C4 * 60L; - static final long C6 = C5 * 24L; - - static final long MAX = Long.MAX_VALUE; - - /** - * Scale d by m, checking for overflow. - * This has a short name to make above code more readable. - */ - static long x(long d, long m, long over) { - if (d > over) return Long.MAX_VALUE; - if (d < -over) return Long.MIN_VALUE; - return d * m; - } - - // To maintain full signature compatibility with 1.5, and to improve the - // clarity of the generated javadoc (see 6287639: Abstract methods in - // enum classes should not be listed as abstract), method convert - // etc. are not declared abstract but otherwise act as abstract methods. - - /** - * Convert the given time duration in the given unit to this - * unit. Conversions from finer to coarser granularities - * truncate, so lose precision. For example converting - * 999 milliseconds to seconds results in - * 0. Conversions from coarser to finer granularities - * with arguments that would numerically overflow saturate to - * Long.MIN_VALUE if negative or Long.MAX_VALUE - * if positive. - * - *

For example, to convert 10 minutes to milliseconds, use: - * TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES) - * - * @param sourceDuration the time duration in the given sourceUnit - * @param sourceUnit the unit of the sourceDuration argument - * @return the converted duration in this unit, - * or Long.MIN_VALUE if conversion would negatively - * overflow, or Long.MAX_VALUE if it would positively overflow. - */ - public long convert(long sourceDuration, TimeUnit2 sourceUnit) { - throw new AbstractMethodError(); - } - - /** - * Convert the given time duration in the given unit to this - * unit. Conversions from finer to coarser granularities - * truncate, so lose precision. For example converting - * 999 milliseconds to seconds results in - * 0. Conversions from coarser to finer granularities - * with arguments that would numerically overflow saturate to - * Long.MIN_VALUE if negative or Long.MAX_VALUE - * if positive. - * - *

For example, to convert 10 minutes to milliseconds, use: - * TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES) - * - * @param sourceDuration the time duration in the given sourceUnit - * @param sourceUnit the unit of the sourceDuration argument - * @return the converted duration in this unit, - * or Long.MIN_VALUE if conversion would negatively - * overflow, or Long.MAX_VALUE if it would positively overflow. - */ - public long convert(long sourceDuration, TimeUnit sourceUnit) { - throw new AbstractMethodError(); - } - - /** - * Equivalent to NANOSECONDS.convert(duration, this). - * @param duration the duration - * @return the converted duration, - * or Long.MIN_VALUE if conversion would negatively - * overflow, or Long.MAX_VALUE if it would positively overflow. - * @see #convert - */ - public long toNanos(long duration) { - throw new AbstractMethodError(); - } - - /** - * Equivalent to MICROSECONDS.convert(duration, this). - * @param duration the duration - * @return the converted duration, - * or Long.MIN_VALUE if conversion would negatively - * overflow, or Long.MAX_VALUE if it would positively overflow. - * @see #convert - */ - public long toMicros(long duration) { - throw new AbstractMethodError(); - } - - /** - * Equivalent to MILLISECONDS.convert(duration, this). - * @param duration the duration - * @return the converted duration, - * or Long.MIN_VALUE if conversion would negatively - * overflow, or Long.MAX_VALUE if it would positively overflow. - * @see #convert - */ - public long toMillis(long duration) { - throw new AbstractMethodError(); - } - - /** - * Equivalent to SECONDS.convert(duration, this). - * @param duration the duration - * @return the converted duration, - * or Long.MIN_VALUE if conversion would negatively - * overflow, or Long.MAX_VALUE if it would positively overflow. - * @see #convert - */ - public long toSeconds(long duration) { - throw new AbstractMethodError(); - } - - /** - * Equivalent to MINUTES.convert(duration, this). - * @param duration the duration - * @return the converted duration, - * or Long.MIN_VALUE if conversion would negatively - * overflow, or Long.MAX_VALUE if it would positively overflow. - * @see #convert - * @since 1.6 - */ - public long toMinutes(long duration) { - throw new AbstractMethodError(); - } - - /** - * Equivalent to HOURS.convert(duration, this). - * @param duration the duration - * @return the converted duration, - * or Long.MIN_VALUE if conversion would negatively - * overflow, or Long.MAX_VALUE if it would positively overflow. - * @see #convert - * @since 1.6 - */ - public long toHours(long duration) { - throw new AbstractMethodError(); - } - - /** - * Equivalent to DAYS.convert(duration, this). - * @param duration the duration - * @return the converted duration - * @see #convert - * @since 1.6 - */ - public long toDays(long duration) { - throw new AbstractMethodError(); - } - - /** - * Utility to compute the excess-nanosecond argument to wait, - * sleep, join. - * @param d the duration - * @param m the number of milliseconds - * @return the number of nanoseconds - */ - abstract int excessNanos(long d, long m); - - /** - * Performs a timed Object.wait using this time unit. - * This is a convenience method that converts timeout arguments - * into the form required by the Object.wait method. - * - *

For example, you could implement a blocking poll - * method (see {@link java.util.concurrent.BlockingQueue#poll BlockingQueue.poll}) - * using: - * - *

  public synchronized Object poll(long timeout, TimeUnit unit) throws InterruptedException {
-     *    while (empty) {
-     *      unit.timedWait(this, timeout);
-     *      ...
-     *    }
-     *  }
- * - * @param obj the object to wait on - * @param timeout the maximum time to wait. If less than - * or equal to zero, do not wait at all. - * @throws InterruptedException if interrupted while waiting. - * @see Object#wait(long, int) - */ - public void timedWait(Object obj, long timeout) - throws InterruptedException { - if (timeout > 0) { - long ms = toMillis(timeout); - int ns = excessNanos(timeout, ms); - obj.wait(ms, ns); - } - } - - /** - * Performs a timed Thread.join using this time unit. - * This is a convenience method that converts time arguments into the - * form required by the Thread.join method. - * @param thread the thread to wait for - * @param timeout the maximum time to wait. If less than - * or equal to zero, do not wait at all. - * @throws InterruptedException if interrupted while waiting. - * @see Thread#join(long, int) - */ - public void timedJoin(Thread thread, long timeout) - throws InterruptedException { - if (timeout > 0) { - long ms = toMillis(timeout); - int ns = excessNanos(timeout, ms); - thread.join(ms, ns); - } - } - - /** - * Performs a Thread.sleep using this unit. - * This is a convenience method that converts time arguments into the - * form required by the Thread.sleep method. - * @param timeout the minimum time to sleep. If less than - * or equal to zero, do not sleep at all. - * @throws InterruptedException if interrupted while sleeping. - * @see Thread#sleep - */ - public void sleep(long timeout) throws InterruptedException { - if (timeout > 0) { - long ms = toMillis(timeout); - int ns = excessNanos(timeout, ms); - Thread.sleep(ms, ns); - } - } - +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/licenses/publicdomain + */ + +package hudson.util; + +import java.util.concurrent.TimeUnit; + +/** + * A TimeUnit represents time durations at a given unit of + * granularity and provides utility methods to convert across units, + * and to perform timing and delay operations in these units. A + * TimeUnit does not maintain time information, but only + * helps organize and use time representations that may be maintained + * separately across various contexts. A nanosecond is defined as one + * thousandth of a microsecond, a microsecond as one thousandth of a + * millisecond, a millisecond as one thousandth of a second, a minute + * as sixty seconds, an hour as sixty minutes, and a day as twenty four + * hours. + * + *

A TimeUnit is mainly used to inform time-based methods + * how a given timing parameter should be interpreted. For example, + * the following code will timeout in 50 milliseconds if the {@link + * java.util.concurrent.locks.Lock lock} is not available: + * + *

  Lock lock = ...;
+ *  if ( lock.tryLock(50L, TimeUnit.MILLISECONDS) ) ...
+ * 
+ * while this code will timeout in 50 seconds: + *
+ *  Lock lock = ...;
+ *  if ( lock.tryLock(50L, TimeUnit.SECONDS) ) ...
+ * 
+ * + * Note however, that there is no guarantee that a particular timeout + * implementation will be able to notice the passage of time at the + * same granularity as the given TimeUnit. + * + * @since 1.5 + * @author Doug Lea + */ +public enum TimeUnit2 { + NANOSECONDS { + public long toNanos(long d) { return d; } + public long toMicros(long d) { return d/(C1/C0); } + public long toMillis(long d) { return d/(C2/C0); } + public long toSeconds(long d) { return d/(C3/C0); } + public long toMinutes(long d) { return d/(C4/C0); } + public long toHours(long d) { return d/(C5/C0); } + public long toDays(long d) { return d/(C6/C0); } + public long convert(long d, TimeUnit2 u) { return u.toNanos(d); } + public long convert(long d, TimeUnit u) { return u.toNanos(d); } + int excessNanos(long d, long m) { return (int)(d - (m*C2)); } + }, + MICROSECONDS { + public long toNanos(long d) { return x(d, C1/C0, MAX/(C1/C0)); } + public long toMicros(long d) { return d; } + public long toMillis(long d) { return d/(C2/C1); } + public long toSeconds(long d) { return d/(C3/C1); } + public long toMinutes(long d) { return d/(C4/C1); } + public long toHours(long d) { return d/(C5/C1); } + public long toDays(long d) { return d/(C6/C1); } + public long convert(long d, TimeUnit2 u) { return u.toMicros(d); } + public long convert(long d, TimeUnit u) { return u.toMicros(d); } + int excessNanos(long d, long m) { return (int)((d*C1) - (m*C2)); } + }, + MILLISECONDS { + public long toNanos(long d) { return x(d, C2/C0, MAX/(C2/C0)); } + public long toMicros(long d) { return x(d, C2/C1, MAX/(C2/C1)); } + public long toMillis(long d) { return d; } + public long toSeconds(long d) { return d/(C3/C2); } + public long toMinutes(long d) { return d/(C4/C2); } + public long toHours(long d) { return d/(C5/C2); } + public long toDays(long d) { return d/(C6/C2); } + public long convert(long d, TimeUnit2 u) { return u.toMillis(d); } + public long convert(long d, TimeUnit u) { return u.toMillis(d); } + int excessNanos(long d, long m) { return 0; } + }, + SECONDS { + public long toNanos(long d) { return x(d, C3/C0, MAX/(C3/C0)); } + public long toMicros(long d) { return x(d, C3/C1, MAX/(C3/C1)); } + public long toMillis(long d) { return x(d, C3/C2, MAX/(C3/C2)); } + public long toSeconds(long d) { return d; } + public long toMinutes(long d) { return d/(C4/C3); } + public long toHours(long d) { return d/(C5/C3); } + public long toDays(long d) { return d/(C6/C3); } + public long convert(long d, TimeUnit2 u) { return u.toSeconds(d); } + public long convert(long d, TimeUnit u) { return u.toSeconds(d); } + int excessNanos(long d, long m) { return 0; } + }, + MINUTES { + public long toNanos(long d) { return x(d, C4/C0, MAX/(C4/C0)); } + public long toMicros(long d) { return x(d, C4/C1, MAX/(C4/C1)); } + public long toMillis(long d) { return x(d, C4/C2, MAX/(C4/C2)); } + public long toSeconds(long d) { return x(d, C4/C3, MAX/(C4/C3)); } + public long toMinutes(long d) { return d; } + public long toHours(long d) { return d/(C5/C4); } + public long toDays(long d) { return d/(C6/C4); } + public long convert(long d, TimeUnit2 u) { return u.toMinutes(d); } + public long convert(long d, TimeUnit u) { return SECONDS.toMinutes(u.toSeconds(d)); } + int excessNanos(long d, long m) { return 0; } + }, + HOURS { + public long toNanos(long d) { return x(d, C5/C0, MAX/(C5/C0)); } + public long toMicros(long d) { return x(d, C5/C1, MAX/(C5/C1)); } + public long toMillis(long d) { return x(d, C5/C2, MAX/(C5/C2)); } + public long toSeconds(long d) { return x(d, C5/C3, MAX/(C5/C3)); } + public long toMinutes(long d) { return x(d, C5/C4, MAX/(C5/C4)); } + public long toHours(long d) { return d; } + public long toDays(long d) { return d/(C6/C5); } + public long convert(long d, TimeUnit2 u) { return u.toHours(d); } + public long convert(long d, TimeUnit u) { return SECONDS.toHours(u.toSeconds(d)); } + int excessNanos(long d, long m) { return 0; } + }, + DAYS { + public long toNanos(long d) { return x(d, C6/C0, MAX/(C6/C0)); } + public long toMicros(long d) { return x(d, C6/C1, MAX/(C6/C1)); } + public long toMillis(long d) { return x(d, C6/C2, MAX/(C6/C2)); } + public long toSeconds(long d) { return x(d, C6/C3, MAX/(C6/C3)); } + public long toMinutes(long d) { return x(d, C6/C4, MAX/(C6/C4)); } + public long toHours(long d) { return x(d, C6/C5, MAX/(C6/C5)); } + public long toDays(long d) { return d; } + public long convert(long d, TimeUnit2 u) { return u.toDays(d); } + public long convert(long d, TimeUnit u) { return SECONDS.toDays(u.toSeconds(d)); } + int excessNanos(long d, long m) { return 0; } + }; + + // Handy constants for conversion methods + static final long C0 = 1L; + static final long C1 = C0 * 1000L; + static final long C2 = C1 * 1000L; + static final long C3 = C2 * 1000L; + static final long C4 = C3 * 60L; + static final long C5 = C4 * 60L; + static final long C6 = C5 * 24L; + + static final long MAX = Long.MAX_VALUE; + + /** + * Scale d by m, checking for overflow. + * This has a short name to make above code more readable. + */ + static long x(long d, long m, long over) { + if (d > over) return Long.MAX_VALUE; + if (d < -over) return Long.MIN_VALUE; + return d * m; + } + + // To maintain full signature compatibility with 1.5, and to improve the + // clarity of the generated javadoc (see 6287639: Abstract methods in + // enum classes should not be listed as abstract), method convert + // etc. are not declared abstract but otherwise act as abstract methods. + + /** + * Convert the given time duration in the given unit to this + * unit. Conversions from finer to coarser granularities + * truncate, so lose precision. For example converting + * 999 milliseconds to seconds results in + * 0. Conversions from coarser to finer granularities + * with arguments that would numerically overflow saturate to + * Long.MIN_VALUE if negative or Long.MAX_VALUE + * if positive. + * + *

For example, to convert 10 minutes to milliseconds, use: + * TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES) + * + * @param sourceDuration the time duration in the given sourceUnit + * @param sourceUnit the unit of the sourceDuration argument + * @return the converted duration in this unit, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. + */ + public long convert(long sourceDuration, TimeUnit2 sourceUnit) { + throw new AbstractMethodError(); + } + + /** + * Convert the given time duration in the given unit to this + * unit. Conversions from finer to coarser granularities + * truncate, so lose precision. For example converting + * 999 milliseconds to seconds results in + * 0. Conversions from coarser to finer granularities + * with arguments that would numerically overflow saturate to + * Long.MIN_VALUE if negative or Long.MAX_VALUE + * if positive. + * + *

For example, to convert 10 minutes to milliseconds, use: + * TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES) + * + * @param sourceDuration the time duration in the given sourceUnit + * @param sourceUnit the unit of the sourceDuration argument + * @return the converted duration in this unit, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. + */ + public long convert(long sourceDuration, TimeUnit sourceUnit) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to NANOSECONDS.convert(duration, this). + * @param duration the duration + * @return the converted duration, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. + * @see #convert + */ + public long toNanos(long duration) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to MICROSECONDS.convert(duration, this). + * @param duration the duration + * @return the converted duration, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. + * @see #convert + */ + public long toMicros(long duration) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to MILLISECONDS.convert(duration, this). + * @param duration the duration + * @return the converted duration, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. + * @see #convert + */ + public long toMillis(long duration) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to SECONDS.convert(duration, this). + * @param duration the duration + * @return the converted duration, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. + * @see #convert + */ + public long toSeconds(long duration) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to MINUTES.convert(duration, this). + * @param duration the duration + * @return the converted duration, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. + * @see #convert + * @since 1.6 + */ + public long toMinutes(long duration) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to HOURS.convert(duration, this). + * @param duration the duration + * @return the converted duration, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. + * @see #convert + * @since 1.6 + */ + public long toHours(long duration) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to DAYS.convert(duration, this). + * @param duration the duration + * @return the converted duration + * @see #convert + * @since 1.6 + */ + public long toDays(long duration) { + throw new AbstractMethodError(); + } + + /** + * Utility to compute the excess-nanosecond argument to wait, + * sleep, join. + * @param d the duration + * @param m the number of milliseconds + * @return the number of nanoseconds + */ + abstract int excessNanos(long d, long m); + + /** + * Performs a timed Object.wait using this time unit. + * This is a convenience method that converts timeout arguments + * into the form required by the Object.wait method. + * + *

For example, you could implement a blocking poll + * method (see {@link java.util.concurrent.BlockingQueue#poll BlockingQueue.poll}) + * using: + * + *

  public synchronized Object poll(long timeout, TimeUnit unit) throws InterruptedException {
+     *    while (empty) {
+     *      unit.timedWait(this, timeout);
+     *      ...
+     *    }
+     *  }
+ * + * @param obj the object to wait on + * @param timeout the maximum time to wait. If less than + * or equal to zero, do not wait at all. + * @throws InterruptedException if interrupted while waiting. + * @see Object#wait(long, int) + */ + public void timedWait(Object obj, long timeout) + throws InterruptedException { + if (timeout > 0) { + long ms = toMillis(timeout); + int ns = excessNanos(timeout, ms); + obj.wait(ms, ns); + } + } + + /** + * Performs a timed Thread.join using this time unit. + * This is a convenience method that converts time arguments into the + * form required by the Thread.join method. + * @param thread the thread to wait for + * @param timeout the maximum time to wait. If less than + * or equal to zero, do not wait at all. + * @throws InterruptedException if interrupted while waiting. + * @see Thread#join(long, int) + */ + public void timedJoin(Thread thread, long timeout) + throws InterruptedException { + if (timeout > 0) { + long ms = toMillis(timeout); + int ns = excessNanos(timeout, ms); + thread.join(ms, ns); + } + } + + /** + * Performs a Thread.sleep using this unit. + * This is a convenience method that converts time arguments into the + * form required by the Thread.sleep method. + * @param timeout the minimum time to sleep. If less than + * or equal to zero, do not sleep at all. + * @throws InterruptedException if interrupted while sleeping. + * @see Thread#sleep + */ + public void sleep(long timeout) throws InterruptedException { + if (timeout > 0) { + long ms = toMillis(timeout); + int ns = excessNanos(timeout, ms); + Thread.sleep(ms, ns); + } + } + } \ No newline at end of file diff --git a/core/src/main/resources/hudson/model/AbstractProject/_api.jelly b/core/src/main/resources/hudson/model/AbstractProject/_api.jelly index 7a9e710fa3ae6a09089f0c28fba88184e7e68ccc..f5e14467d4baace93eae505278ee74a84f460a55 100644 --- a/core/src/main/resources/hudson/model/AbstractProject/_api.jelly +++ b/core/src/main/resources/hudson/model/AbstractProject/_api.jelly @@ -1,9 +1,9 @@ - - - -

Disable/Enable a job

-

- To programmatically disable a job, post to this URL. - Similarly post to this URL for enabling this job. -

+ + + +

Disable/Enable a job

+

+ To programmatically disable a job, post to this URL. + Similarly post to this URL for enabling this job. +

\ No newline at end of file diff --git a/core/src/main/resources/hudson/model/Job/jobpropertysummaries.jelly b/core/src/main/resources/hudson/model/Job/jobpropertysummaries.jelly index 21ef07739ff5b414e905cfa7eb7eb89af62db501..c1e960cc1480fe0d312de4fa797b6f614ca10709 100644 --- a/core/src/main/resources/hudson/model/Job/jobpropertysummaries.jelly +++ b/core/src/main/resources/hudson/model/Job/jobpropertysummaries.jelly @@ -1,7 +1,7 @@ - - - + + + - \ No newline at end of file +
diff --git a/core/src/main/resources/hudson/model/MyView/noJob.jelly b/core/src/main/resources/hudson/model/MyView/noJob.jelly index 858a75f4233a798d7a8348dab149ae0b9cf61dce..713c258cbeaade9c069ceec015bdae26a0f4068e 100644 --- a/core/src/main/resources/hudson/model/MyView/noJob.jelly +++ b/core/src/main/resources/hudson/model/MyView/noJob.jelly @@ -1,3 +1,3 @@ -
- This view has no jobs. +
+ This view has no jobs.
\ No newline at end of file diff --git a/core/src/main/resources/hudson/model/ParameterDefinition/config.jelly b/core/src/main/resources/hudson/model/ParameterDefinition/config.jelly index 39f265428262252398f6f31530ebc191a1f57f58..0fa5515d918e047afb06e80b3d18b49ac7477451 100644 --- a/core/src/main/resources/hudson/model/ParameterDefinition/config.jelly +++ b/core/src/main/resources/hudson/model/ParameterDefinition/config.jelly @@ -1,8 +1,8 @@ - - - + + + \ No newline at end of file diff --git a/core/src/main/resources/hudson/model/ParametersAction/index.jelly b/core/src/main/resources/hudson/model/ParametersAction/index.jelly index b5e1011ff3e45b6ae036cea68ceabd388335903a..ca243f33e54bc23aef74a71993e0fc21ae664640 100644 --- a/core/src/main/resources/hudson/model/ParametersAction/index.jelly +++ b/core/src/main/resources/hudson/model/ParametersAction/index.jelly @@ -1,16 +1,16 @@ - - - - -

${%Build} #${it.build.number}

- - - - - -
-
-
+ + + + +

${%Build} #${it.build.number}

+ + + + + +
+
+
diff --git a/core/src/main/resources/hudson/model/ParametersDefinitionProperty/config.jelly b/core/src/main/resources/hudson/model/ParametersDefinitionProperty/config.jelly index 2f76a7c770cfe4d8654e54c7b557aef0441e14c9..a8d1f2a2c4ca8af1f56aa638abdf72bbf1b7c407 100644 --- a/core/src/main/resources/hudson/model/ParametersDefinitionProperty/config.jelly +++ b/core/src/main/resources/hudson/model/ParametersDefinitionProperty/config.jelly @@ -1,12 +1,12 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/core/src/main/resources/hudson/model/ParametersDefinitionProperty/index.jelly b/core/src/main/resources/hudson/model/ParametersDefinitionProperty/index.jelly index 39deff7d6e3f8691425dd77185c26e6eb97b0b1a..ecc048c0a1f08fdd059c84f9b7d9c724715b514b 100644 --- a/core/src/main/resources/hudson/model/ParametersDefinitionProperty/index.jelly +++ b/core/src/main/resources/hudson/model/ParametersDefinitionProperty/index.jelly @@ -1,28 +1,28 @@ - - - - - - - -

${it.owner.pronoun} ${it.owner.displayName}

-

${%description}

- - - - - - - - -
-
+ + + + + + + +

${it.owner.pronoun} ${it.owner.displayName}

+

${%description}

+ + + + + + + + +
+
\ No newline at end of file diff --git a/core/src/main/resources/hudson/model/StringParameterDefinition/config.jelly b/core/src/main/resources/hudson/model/StringParameterDefinition/config.jelly index ef5e2fcb0e484feb662e25f10ae1ad3cdccbf42b..c5afe718457d95fc216c8f5074bff26cb718c7f7 100644 --- a/core/src/main/resources/hudson/model/StringParameterDefinition/config.jelly +++ b/core/src/main/resources/hudson/model/StringParameterDefinition/config.jelly @@ -1,10 +1,10 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/core/src/main/resources/hudson/model/StringParameterDefinition/index.jelly b/core/src/main/resources/hudson/model/StringParameterDefinition/index.jelly index 65bba58487f7e7c55e501208a368bc2da35b1f50..743c95ec6ac1c6e0f1a27c817e612b79d6df44e6 100644 --- a/core/src/main/resources/hudson/model/StringParameterDefinition/index.jelly +++ b/core/src/main/resources/hudson/model/StringParameterDefinition/index.jelly @@ -1,10 +1,10 @@ - - -
- - -
-
+ + +
+ + +
+
\ No newline at end of file diff --git a/core/src/main/resources/hudson/model/StringParameterValue/value.jelly b/core/src/main/resources/hudson/model/StringParameterValue/value.jelly index 30c526a01ec3ca41198b0ea1a93c6b070960dc7f..edb4448b5c28a8a47ad5a1c8857a0fb964d8bbf9 100644 --- a/core/src/main/resources/hudson/model/StringParameterValue/value.jelly +++ b/core/src/main/resources/hudson/model/StringParameterValue/value.jelly @@ -1,7 +1,7 @@ - - - - + + + + \ No newline at end of file diff --git a/core/src/main/resources/hudson/security/AuthorizationMatrixProperty/config.jelly b/core/src/main/resources/hudson/security/AuthorizationMatrixProperty/config.jelly index 271d0d805a4a41b1c9fda78a6ee1b0fe703294bb..94921fe38585672187951bc64230d9be8569efae 100644 --- a/core/src/main/resources/hudson/security/AuthorizationMatrixProperty/config.jelly +++ b/core/src/main/resources/hudson/security/AuthorizationMatrixProperty/config.jelly @@ -1,5 +1,5 @@ - - - - + + + + \ No newline at end of file diff --git a/core/src/main/resources/hudson/slaves/CommandLauncher/config.jelly b/core/src/main/resources/hudson/slaves/CommandLauncher/config.jelly index 2b6f5e851c2a44e563fb0f8c61e425d4aa1eafea..a272be29aabad069fb4bf3e17a2fd958e21674e4 100644 --- a/core/src/main/resources/hudson/slaves/CommandLauncher/config.jelly +++ b/core/src/main/resources/hudson/slaves/CommandLauncher/config.jelly @@ -1,5 +1,5 @@ - - - - + + + + \ No newline at end of file diff --git a/core/src/main/resources/hudson/slaves/JNLPLauncher/config.jelly b/core/src/main/resources/hudson/slaves/JNLPLauncher/config.jelly index f5fd5ff00c987b20cb33d61241994e7183e3e46e..271589342d93f6d90f311fe298f9d5b7a2d56401 100644 --- a/core/src/main/resources/hudson/slaves/JNLPLauncher/config.jelly +++ b/core/src/main/resources/hudson/slaves/JNLPLauncher/config.jelly @@ -1,7 +1,7 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/core/src/main/resources/hudson/slaves/RetentionStrategy/Demand/config.jelly b/core/src/main/resources/hudson/slaves/RetentionStrategy/Demand/config.jelly index e85dec51e772a4281be101c835c9bb0556fc88b3..8c1401bf294e1499ef95ad3830f78e1e8b0329b2 100644 --- a/core/src/main/resources/hudson/slaves/RetentionStrategy/Demand/config.jelly +++ b/core/src/main/resources/hudson/slaves/RetentionStrategy/Demand/config.jelly @@ -1,13 +1,13 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/core/src/main/resources/hudson/slaves/RetentionStrategy/Scheduled/config.jelly b/core/src/main/resources/hudson/slaves/RetentionStrategy/Scheduled/config.jelly index 5f4b51dcfe1dad70590275cee6f6da984415208f..ed0f4ec25cd5e335da96662db8fe6ca379b80551 100644 --- a/core/src/main/resources/hudson/slaves/RetentionStrategy/Scheduled/config.jelly +++ b/core/src/main/resources/hudson/slaves/RetentionStrategy/Scheduled/config.jelly @@ -1,9 +1,9 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/core/src/main/resources/hudson/slaves/RetentionStrategy/config.jelly b/core/src/main/resources/hudson/slaves/RetentionStrategy/config.jelly index d70f1e97a8d2def4743e8e28a0b27f34eb6d2e93..2d96bad6694191d65ce5a20604f57eec47910060 100644 --- a/core/src/main/resources/hudson/slaves/RetentionStrategy/config.jelly +++ b/core/src/main/resources/hudson/slaves/RetentionStrategy/config.jelly @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/core/src/main/resources/lib/layout/hasPermission.jelly b/core/src/main/resources/lib/layout/hasPermission.jelly index 84f7452e9c3a21b8a00da022d7e3cc3c69baf15c..0df9a4b005d22da62f702da5f2589dd3235432a5 100644 --- a/core/src/main/resources/lib/layout/hasPermission.jelly +++ b/core/src/main/resources/lib/layout/hasPermission.jelly @@ -1,9 +1,9 @@ - - - - - + + + + + \ No newline at end of file