| author | Henrik Lynggaard Hansen | 2012-07-08 09:33:36 (EDT) |
|---|---|---|
| committer | Henrik Lynggaard Hansen | 2012-07-08 09:33:36 (EDT) |
| commit | 615596af03777514e82ad1653aa61193d5bc6741 (patch) (side-by-side diff) | |
| tree | 4d11c8fd7f205d8f65ad5e4b9fb42645ac3a21ec | |
| parent | 6b2a021db07f935226235132973fb64223c07d91 (diff) | |
| download | org.eclipse.hudson.core-615596af03777514e82ad1653aa61193d5bc6741.zip org.eclipse.hudson.core-615596af03777514e82ad1653aa61193d5bc6741.tar.gz org.eclipse.hudson.core-615596af03777514e82ad1653aa61193d5bc6741.tar.bz2 | |
Run alt-shift-5 to format some of the files with most warningsrefs/changes/68/6668/2
Change-Id: I0092fbb780a85ff1145950e07c73db8afec8409b
Signed-off-by: Henrik Lynggaard Hansen <henrik@hlyh.dk>
| -rw-r--r-- | hudson-core/src/main/java/hudson/FilePath.java | 984 | ||||
| -rw-r--r-- | hudson-core/src/main/java/hudson/Functions.java | 712 | ||||
| -rw-r--r-- | hudson-core/src/main/java/hudson/PluginManager.java | 328 | ||||
| -rw-r--r-- | hudson-core/src/main/java/hudson/model/Queue.java | 1061 | ||||
| -rw-r--r-- | hudson-core/src/main/java/hudson/model/Run.java | 943 |
5 files changed, 2213 insertions, 1815 deletions
diff --git a/hudson-core/src/main/java/hudson/FilePath.java b/hudson-core/src/main/java/hudson/FilePath.java index d0845b4..030e56a 100644 --- a/hudson-core/src/main/java/hudson/FilePath.java +++ b/hudson-core/src/main/java/hudson/FilePath.java @@ -7,10 +7,10 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * - * Contributors: + * Contributors: * * Kohsuke Kawaguchi, Eric Lefevre-Ardant, Erik Ramfelt, Michael B. Donohue, Alan Harder, Manufacture Francaise des Pneumatiques Michelin, Romain Seguy, Winston Prakash, Anton Kozak - * + * * *******************************************************************************/ @@ -93,38 +93,33 @@ import org.kohsuke.stapler.Stapler; import static hudson.FilePath.TarCompression.GZIP; import static hudson.Util.fixEmpty; - /** * {@link File} like object with remoting support. * - * <p> - * Unlike {@link File}, which always implies a file path on the current computer, - * {@link FilePath} represents a file path on a specific slave or the master. + * <p> Unlike {@link File}, which always implies a file path on the current + * computer, {@link FilePath} represents a file path on a specific slave or the + * master. * * Despite that, {@link FilePath} can be used much like {@link File}. It exposes * a bunch of operations (and we should add more operations as long as they are - * generally useful), and when invoked against a file on a remote node, {@link FilePath} - * executes the necessary code remotely, thereby providing semi-transparent file - * operations. + * generally useful), and when invoked against a file on a remote node, + * {@link FilePath} executes the necessary code remotely, thereby providing + * semi-transparent file operations. * - * <h2>Using {@link FilePath} smartly</h2> - * <p> - * The transparency makes it easy to write plugins without worrying too much about - * remoting, by making it works like NFS, where remoting happens at the file-system - * layer. + * <h2>Using {@link FilePath} smartly</h2> <p> The transparency makes it easy to + * write plugins without worrying too much about remoting, by making it works + * like NFS, where remoting happens at the file-system layer. * - * <p> - * But one should note that such use of remoting may not be optional. Sometimes, - * it makes more sense to move some computation closer to the data, as opposed to - * move the data to the computation. For example, if you are just computing a MD5 - * digest of a file, then it would make sense to do the digest on the host where - * the file is located, as opposed to send the whole data to the master and do MD5 - * digesting there. + * <p> But one should note that such use of remoting may not be optional. + * Sometimes, it makes more sense to move some computation closer to the data, + * as opposed to move the data to the computation. For example, if you are just + * computing a MD5 digest of a file, then it would make sense to do the digest + * on the host where the file is located, as opposed to send the whole data to + * the master and do MD5 digesting there. * - * <p> - * {@link FilePath} supports this "code migration" by in the - * {@link #act(FileCallable)} method. One can pass in a custom implementation - * of {@link FileCallable}, to be executed on the node where the data is located. + * <p> {@link FilePath} supports this "code migration" by in the + * {@link #act(FileCallable)} method. One can pass in a custom implementation of + * {@link FileCallable}, to be executed on the node where the data is located. * The following code shows the example: * * <pre> @@ -142,44 +137,43 @@ import static hudson.Util.fixEmpty; * }); * </pre> * - * <p> - * When {@link FileCallable} is transfered to a remote node, it will be done so - * by using the same Java serialization scheme that the remoting module uses. - * See {@link Channel} for more about this. + * <p> When {@link FileCallable} is transfered to a remote node, it will be done + * so by using the same Java serialization scheme that the remoting module uses. + * See {@link Channel} for more about this. * - * <p> - * {@link FilePath} itself can be sent over to a remote node as a part of {@link Callable} - * serialization. For example, sending a {@link FilePath} of a remote node to that - * node causes {@link FilePath} to become "local". Similarly, sending a - * {@link FilePath} that represents the local computer causes it to become "remote." + * <p> {@link FilePath} itself can be sent over to a remote node as a part of + * {@link Callable} serialization. For example, sending a {@link FilePath} of a + * remote node to that node causes {@link FilePath} to become "local". + * Similarly, sending a {@link FilePath} that represents the local computer + * causes it to become "remote." * * @author Kohsuke Kawaguchi */ public final class FilePath implements Serializable { + /** - * When this {@link FilePath} represents the remote path, - * this field is always non-null on master (the field represents - * the channel to the remote slave.) When transferred to a slave via remoting, - * this field reverts back to null, since it's transient. + * When this {@link FilePath} represents the remote path, this field is + * always non-null on master (the field represents the channel to the remote + * slave.) When transferred to a slave via remoting, this field reverts back + * to null, since it's transient. * - * When this {@link FilePath} represents a path on the master, - * this field is null on master. When transferred to a slave via remoting, - * this field becomes non-null, representing the {@link Channel} - * back to the master. + * When this {@link FilePath} represents a path on the master, this field is + * null on master. When transferred to a slave via remoting, this field + * becomes non-null, representing the {@link Channel} back to the master. * - * This is used to determine whether we are running on the master or the slave. + * This is used to determine whether we are running on the master or the + * slave. */ private transient VirtualChannel channel; - // since the platform of the slave might be different, can't use java.io.File private final String remote; /** * Creates a {@link FilePath} that represents a path on the given node. * - * @param channel - * To create a path that represents a remote path, pass in a {@link Channel} - * that's connected to that machine. If null, that means the local file path. + * @param channel To create a path that represents a remote path, pass in a + * {@link Channel} that's connected to that machine. If null, that means the + * local file path. */ public FilePath(VirtualChannel channel, String remote) { this.channel = channel; @@ -189,8 +183,7 @@ public final class FilePath implements Serializable { /** * To create {@link FilePath} that represents a "local" path. * - * <p> - * A "local" path means a file path on the computer where the + * <p> A "local" path means a file path on the computer where the * constructor invocation happened. */ public FilePath(File localPath) { @@ -200,33 +193,32 @@ public final class FilePath implements Serializable { /** * Construct a path starting with a base location. + * * @param base starting point for resolution, and defines channel * @param rel a path which if relative will be resolved against base */ public FilePath(FilePath base, String rel) { this.channel = base.channel; - if(isAbsolute(rel)) { + if (isAbsolute(rel)) { // absolute this.remote = normalize(rel); - } else - if(base.isUnix()) { - this.remote = normalize(base.remote+'/'+rel); + } else if (base.isUnix()) { + this.remote = normalize(base.remote + '/' + rel); } else { //Normalize rel path for windows environment. See http://issues.hudson-ci.org/browse/HUDSON-5084 - this.remote = normalize(base.remote+'\\'+ StringUtils.replace(rel, "/", "\\")); + this.remote = normalize(base.remote + '\\' + StringUtils.replace(rel, "/", "\\")); } } private static boolean isAbsolute(String rel) { return rel.startsWith("/") || DRIVE_PATTERN.matcher(rel).matches(); } - private static final Pattern DRIVE_PATTERN = Pattern.compile("[A-Za-z]:[\\\\/].*"), ABSOLUTE_PREFIX_PATTERN = Pattern.compile("^(\\\\\\\\|(?:[A-Za-z]:)?[\\\\/])[\\\\/]*"); /** - * {@link File#getParent()} etc cannot handle ".." and "." in the path component very well, - * so remove them. + * {@link File#getParent()} etc cannot handle ".." and "." in the path + * component very well, so remove them. */ private static String normalize(String path) { StringBuilder buf = new StringBuilder(); @@ -246,40 +238,60 @@ public final class FilePath implements Serializable { tokens.add(path.substring(s, i)); s = i; // Skip any extra separator chars - while (++i < end && ((c = path.charAt(i)) == '/' || c == '\\')) { } + while (++i < end && ((c = path.charAt(i)) == '/' || c == '\\')) { + } // Add token for separator unless we reached the end - if (i < end) tokens.add(path.substring(s, s+1)); + if (i < end) { + tokens.add(path.substring(s, s + 1)); + } s = i; } } - if (s < end) tokens.add(path.substring(s)); + if (s < end) { + tokens.add(path.substring(s)); + } // Look through tokens for "." or ".." for (int i = 0; i < tokens.size();) { String token = tokens.get(i); if (token.equals(".")) { tokens.remove(i); - if (tokens.size() > 0) + if (tokens.size() > 0) { tokens.remove(i > 0 ? i - 1 : i); + } } else if (token.equals("..")) { if (i == 0) { // If absolute path, just remove: /../something // If relative path, not collapsible so leave as-is tokens.remove(0); - if (tokens.size() > 0) token += tokens.remove(0); - if (!isAbsolute) buf.append(token); + if (tokens.size() > 0) { + token += tokens.remove(0); + } + if (!isAbsolute) { + buf.append(token); + } } else { // Normalize: remove something/.. plus separator before/after i -= 2; - for (int j = 0; j < 3; j++) tokens.remove(i); - if (i > 0) tokens.remove(i-1); - else if (tokens.size() > 0) tokens.remove(0); + for (int j = 0; j < 3; j++) { + tokens.remove(i); + } + if (i > 0) { + tokens.remove(i - 1); + } else if (tokens.size() > 0) { + tokens.remove(0); + } } - } else + } else { i += 2; + } } // Recombine tokens - for (String token : tokens) buf.append(token); - if (buf.length() == 0) buf.append('.'); + for (String token : tokens) { + buf.append(token); + } + if (buf.length() == 0) { + buf.append('.'); + } return buf.toString(); } @@ -288,18 +300,20 @@ public final class FilePath implements Serializable { */ private boolean isUnix() { // if the path represents a local path, there' no need to guess. - if(!isRemote()) - return File.pathSeparatorChar!=';'; - + if (!isRemote()) { + return File.pathSeparatorChar != ';'; + } + // note that we can't use the usual File.pathSeparator and etc., as the OS of // the machine where this code runs and the OS that this FilePath refers to may be different. // Windows absolute path is 'X:\...', so this is usually a good indication of Windows path - if(remote.length()>3 && remote.charAt(1)==':' && remote.charAt(2)=='\\') + if (remote.length() > 3 && remote.charAt(1) == ':' && remote.charAt(2) == '\\') { return false; + } // Windows can handle '/' as a path separator but Unix can't, // so err on Unix side - return remote.indexOf("\\")==-1; + return remote.indexOf("\\") == -1; } public String getRemote() { @@ -307,114 +321,117 @@ public final class FilePath implements Serializable { } /** - * Creates a zip file from this directory or a file and sends that to the given output stream. + * Creates a zip file from this directory or a file and sends that to the + * given output stream. * - * @deprecated as of 1.315. Use {@link #zip(OutputStream)} that has more consistent name. + * @deprecated as of 1.315. Use {@link #zip(OutputStream)} that has more + * consistent name. */ public void createZipArchive(OutputStream os) throws IOException, InterruptedException { zip(os); } /** - * Creates a zip file from this directory or a file and sends that to the given output stream. + * Creates a zip file from this directory or a file and sends that to the + * given output stream. */ public void zip(OutputStream os) throws IOException, InterruptedException { - zip(os,(FileFilter)null); + zip(os, (FileFilter) null); } /** - * Creates a zip file from this directory by using the specified filter, - * and sends the result to the given output stream. + * Creates a zip file from this directory by using the specified filter, and + * sends the result to the given output stream. * - * @param filter - * Must be serializable since it may be executed remotely. Can be null to add all files. + * @param filter Must be serializable since it may be executed remotely. Can + * be null to add all files. * * @since 1.315 */ public void zip(OutputStream os, FileFilter filter) throws IOException, InterruptedException { - archive(ArchiverFactory.ZIP,os,filter); + archive(ArchiverFactory.ZIP, os, filter); } /** - * Creates a zip file from this directory by only including the files that match the given glob. + * Creates a zip file from this directory by only including the files that + * match the given glob. * - * @param glob - * Ant style glob, like "**/*.xml". If empty or null, this method - * works like {@link #createZipArchive(OutputStream)} + * @param glob Ant style glob, like "**/*.xml". If empty or null, this + * method works like {@link #createZipArchive(OutputStream)} * * @since 1.129 - * @deprecated as of 1.315 - * Use {@link #zip(OutputStream,String)} that has more consistent name. + * @deprecated as of 1.315 Use {@link #zip(OutputStream,String)} that has + * more consistent name. */ public void createZipArchive(OutputStream os, final String glob) throws IOException, InterruptedException { - archive(ArchiverFactory.ZIP,os,glob); + archive(ArchiverFactory.ZIP, os, glob); } /** - * Creates a zip file from this directory by only including the files that match the given glob. + * Creates a zip file from this directory by only including the files that + * match the given glob. * - * @param glob - * Ant style glob, like "**/*.xml". If empty or null, this method - * works like {@link #createZipArchive(OutputStream)} + * @param glob Ant style glob, like "**/*.xml". If empty or null, this + * method works like {@link #createZipArchive(OutputStream)} * * @since 1.315 */ public void zip(OutputStream os, final String glob) throws IOException, InterruptedException { - archive(ArchiverFactory.ZIP,os,glob); + archive(ArchiverFactory.ZIP, os, glob); } /** - * Uses the given scanner on 'this' directory to list up files and then archive it to a zip stream. + * Uses the given scanner on 'this' directory to list up files and then + * archive it to a zip stream. */ public int zip(OutputStream out, DirScanner scanner) throws IOException, InterruptedException { return archive(ArchiverFactory.ZIP, out, scanner); } /** - * Archives this directory into the specified archive format, to the given {@link OutputStream}, by using - * {@link DirScanner} to choose what files to include. + * Archives this directory into the specified archive format, to the given + * {@link OutputStream}, by using {@link DirScanner} to choose what files to + * include. * - * @return - * number of files/directories archived. This is only really useful to check for a situation where nothing - * is archived. + * @return number of files/directories archived. This is only really useful + * to check for a situation where nothing is archived. */ public int archive(final ArchiverFactory factory, OutputStream os, final DirScanner scanner) throws IOException, InterruptedException { - final OutputStream out = (channel!=null)?new RemoteOutputStream(os):os; + final OutputStream out = (channel != null) ? new RemoteOutputStream(os) : os; return act(new FileCallable<Integer>() { public Integer invoke(File f, VirtualChannel channel) throws IOException { Archiver a = factory.create(out); try { - scanner.scan(f,a); + scanner.scan(f, a); } finally { a.close(); } return a.countEntries(); } - private static final long serialVersionUID = 1L; }); } private int archive(final ArchiverFactory factory, OutputStream os, final FileFilter filter) throws IOException, InterruptedException { - return archive(factory,os,new DirScanner.Filter(filter)); + return archive(factory, os, new DirScanner.Filter(filter)); } private int archive(final ArchiverFactory factory, OutputStream os, final String glob) throws IOException, InterruptedException { - return archive(factory,os,new DirScanner.Glob(glob,null)); + return archive(factory, os, new DirScanner.Glob(glob, null)); } /** * When this {@link FilePath} represents a zip file, extracts that zip file. * - * @param target - * Target directory to expand files to. All the necessary directories will be created. + * @param target Target directory to expand files to. All the necessary + * directories will be created. * @since 1.248 * @see #unzipFrom(InputStream) */ public void unzip(final FilePath target) throws IOException, InterruptedException { target.act(new FileCallable<Void>() { public Void invoke(File dir, VirtualChannel channel) throws IOException { - unzip(dir,FilePath.this.read()); + unzip(dir, FilePath.this.read()); return null; } private static final long serialVersionUID = 1L; @@ -424,17 +441,16 @@ public final class FilePath implements Serializable { /** * When this {@link FilePath} represents a tar file, extracts that tar file. * - * @param target - * Target directory to expand files to. All the necessary directories will be created. - * @param compression - * Compression mode of this tar file. + * @param target Target directory to expand files to. All the necessary + * directories will be created. + * @param compression Compression mode of this tar file. * @since 1.292 * @see #untarFrom(InputStream, TarCompression) */ public void untar(final FilePath target, final TarCompression compression) throws IOException, InterruptedException { target.act(new FileCallable<Void>() { public Void invoke(File dir, VirtualChannel channel) throws IOException { - readFromTar(FilePath.this.getName(),dir,compression.extract(FilePath.this.read())); + readFromTar(FilePath.this.getName(), dir, compression.extract(FilePath.this.read())); return null; } private static final long serialVersionUID = 1L; @@ -442,10 +458,11 @@ public final class FilePath implements Serializable { } /** - * Reads the given InputStream as a zip file and extracts it into this directory. + * Reads the given InputStream as a zip file and extracts it into this + * directory. * - * @param _in - * The stream will be closed by this method after it's fully read. + * @param _in The stream will be closed by this method after it's fully + * read. * @since 1.283 * @see #unzip(FilePath) */ @@ -466,13 +483,15 @@ public final class FilePath implements Serializable { java.util.zip.ZipEntry e; try { - while((e=zip.getNextEntry())!=null) { - File f = new File(dir,e.getName()); - if(e.isDirectory()) { + while ((e = zip.getNextEntry()) != null) { + File f = new File(dir, e.getName()); + if (e.isDirectory()) { f.mkdirs(); } else { File p = f.getParentFile(); - if(p!=null) p.mkdirs(); + if (p != null) { + p.mkdirs(); + } IOUtils.copy(zip, f); f.setLastModified(e.getTime()); zip.closeEntry(); @@ -487,7 +506,7 @@ public final class FilePath implements Serializable { * Absolutizes this {@link FilePath} and returns the new one. */ public FilePath absolutize() throws IOException, InterruptedException { - return new FilePath(channel,act(new FileCallable<String>() { + return new FilePath(channel, act(new FileCallable<String>() { public String invoke(File f, VirtualChannel channel) throws IOException { return f.getAbsolutePath(); } @@ -496,12 +515,18 @@ public final class FilePath implements Serializable { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } FilePath that = (FilePath) o; - if (channel != null ? !channel.equals(that.channel) : that.channel != null) return false; + if (channel != null ? !channel.equals(that.channel) : that.channel != null) { + return false; + } return remote.equals(that.remote); } @@ -510,30 +535,33 @@ public final class FilePath implements Serializable { public int hashCode() { return 31 * (channel != null ? channel.hashCode() : 0) + remote.hashCode(); } - + /** * Supported tar file compression methods. */ public enum TarCompression { + NONE { public InputStream extract(InputStream in) { return in; } + public OutputStream compress(OutputStream out) { return out; } }, GZIP { public InputStream extract(InputStream _in) throws IOException { - HeadBufferingStream in = new HeadBufferingStream(_in,SIDE_BUFFER_SIZE); + HeadBufferingStream in = new HeadBufferingStream(_in, SIDE_BUFFER_SIZE); try { return new GZIPInputStream(in, BUFFER_SIZE); } catch (IOException e) { // various people reported "java.io.IOException: Not in GZIP format" here, so diagnose this problem better in.fillSide(); - throw new IOException2(e.getMessage()+"\nstream="+Util.toHexString(in.getSideBuffer()),e); + throw new IOException2(e.getMessage() + "\nstream=" + Util.toHexString(in.getSideBuffer()), e); } } + public OutputStream compress(OutputStream out) throws IOException { return new GZIPOutputStream(new BufferedOutputStream(out)); } @@ -541,16 +569,17 @@ public final class FilePath implements Serializable { private static final int BUFFER_SIZE = 8192; public abstract InputStream extract(InputStream in) throws IOException; + public abstract OutputStream compress(OutputStream in) throws IOException; } /** - * Reads the given InputStream as a tar file and extracts it into this directory. + * Reads the given InputStream as a tar file and extracts it into this + * directory. * - * @param _in - * The stream will be closed by this method after it's fully read. - * @param compression - * The compression method in use. + * @param _in The stream will be closed by this method after it's fully + * read. + * @param compression The compression method in use. * @since 1.292 */ public void untarFrom(InputStream _in, final TarCompression compression) throws IOException, InterruptedException { @@ -558,7 +587,7 @@ public final class FilePath implements Serializable { final InputStream in = new RemoteInputStream(_in); act(new FileCallable<Void>() { public Void invoke(File dir, VirtualChannel channel) throws IOException { - readFromTar("input stream",dir, compression.extract(in)); + readFromTar("input stream", dir, compression.extract(in)); return null; } private static final long serialVersionUID = 1L; @@ -569,29 +598,27 @@ public final class FilePath implements Serializable { } /** - * Given a tgz/zip file, extracts it to the given target directory, if necessary. + * Given a tgz/zip file, extracts it to the given target directory, if + * necessary. * - * <p> - * This method is a convenience method designed for installing a binary package to a location - * that supports upgrade and downgrade. Specifically, + * <p> This method is a convenience method designed for installing a binary + * package to a location that supports upgrade and downgrade. Specifically, * - * <ul> - * <li>If the target directory doesn't exist {@linkplain #mkdirs() it'll be created}. - * <li>The timestamp of the .tgz file is left in the installation directory upon extraction. - * <li>If the timestamp left in the directory doesn't match with the timestamp of the current archive file, - * the directory contents will be discarded and the archive file will be re-extracted. - * <li>If the connection is refused but the target directory already exists, it is left alone. - * </ul> + * <ul> <li>If the target directory doesn't exist + * {@linkplain #mkdirs() it'll be created}. <li>The timestamp of the .tgz + * file is left in the installation directory upon extraction. <li>If the + * timestamp left in the directory doesn't match with the timestamp of the + * current archive file, the directory contents will be discarded and the + * archive file will be re-extracted. <li>If the connection is refused but + * the target directory already exists, it is left alone. </ul> * - * @param archive - * The resource that represents the tgz/zip file. This URL must support the "Last-Modified" header. - * (Most common usage is to get this from {@link ClassLoader#getResource(String)}) - * @param listener - * If non-null, a message will be printed to this listener once this method decides to - * extract an archive. - * @return - * true if the archive was extracted. false if the extraction was skipped because the target directory - * was considered up to date. + * @param archive The resource that represents the tgz/zip file. This URL + * must support the "Last-Modified" header. (Most common usage is to get + * this from {@link ClassLoader#getResource(String)}) + * @param listener If non-null, a message will be printed to this listener + * once this method decides to extract an archive. + * @return true if the archive was extracted. false if the extraction was + * skipped because the target directory was considered up to date. * @since 1.299 */ public boolean installIfNecessaryFrom(URL archive, TaskListener listener, String message) throws IOException, InterruptedException { @@ -614,39 +641,42 @@ public final class FilePath implements Serializable { long sourceTimestamp = con.getLastModified(); FilePath timestamp = this.child(".timestamp"); - if(this.exists()) { - if(timestamp.exists() && sourceTimestamp ==timestamp.lastModified()) + if (this.exists()) { + if (timestamp.exists() && sourceTimestamp == timestamp.lastModified()) { return false; // already up to date + } this.deleteContents(); } else { this.mkdirs(); } - if(listener!=null) + if (listener != null) { listener.getLogger().println(message); + } InputStream in = con.getInputStream(); CountingInputStream cis = new CountingInputStream(in); - + try { - if(archive.toExternalForm().endsWith(".zip")) + if (archive.toExternalForm().endsWith(".zip")) { unzipFrom(cis); - else - untarFrom(cis,GZIP); + } else { + untarFrom(cis, GZIP); + } } catch (IOException e) { throw new IOException2(String.format("Failed to unpack %s (%d bytes read of total %d)", - archive,cis.getByteCount(),con.getContentLength()),e); + archive, cis.getByteCount(), con.getContentLength()), e); } timestamp.touch(sourceTimestamp); return true; } catch (IOException e) { - throw new IOException2("Failed to install "+archive+" to "+remote,e); + throw new IOException2("Failed to install " + archive + " to " + remote, e); } } /** - * Reads the URL on the current VM, and writes all the data to this {@link FilePath} - * (this is different from resolving URL remotely.) + * Reads the URL on the current VM, and writes all the data to this + * {@link FilePath} (this is different from resolving URL remotely.) * * @since 1.293 */ @@ -660,7 +690,8 @@ public final class FilePath implements Serializable { } /** - * Replaces the content of this file by the data from the given {@link InputStream}. + * Replaces the content of this file by the data from the given + * {@link InputStream}. * * @since 1.293 */ @@ -675,7 +706,7 @@ public final class FilePath implements Serializable { /** * Conveniene method to call {@link FilePath#copyTo(FilePath)}. - * + * * @since 1.311 */ public void copyFrom(FilePath src) throws IOException, InterruptedException { @@ -683,10 +714,11 @@ public final class FilePath implements Serializable { } /** - * Place the data from {@link FileItem} into the file location specified by this {@link FilePath} object. + * Place the data from {@link FileItem} into the file location specified by + * this {@link FilePath} object. */ public void copyFrom(FileItem file) throws IOException, InterruptedException { - if(channel==null) { + if (channel == null) { try { file.write(new File(remote)); } catch (IOException e) { @@ -698,7 +730,7 @@ public final class FilePath implements Serializable { InputStream i = file.getInputStream(); OutputStream o = write(); try { - IOUtils.copy(i,o); + IOUtils.copy(i, o); } finally { o.close(); i.close(); @@ -707,23 +739,24 @@ public final class FilePath implements Serializable { } /** - * Code that gets executed on the machine where the {@link FilePath} is local. - * Used to act on {@link FilePath}. + * Code that gets executed on the machine where the {@link FilePath} is + * local. Used to act on {@link FilePath}. * * @see FilePath#act(FileCallable) */ public static interface FileCallable<T> extends Serializable { + /** - * Performs the computational task on the node where the data is located. + * Performs the computational task on the node where the data is + * located. * - * <p> - * All the exceptions are forwarded to the caller. + * <p> All the exceptions are forwarded to the caller. * - * @param f - * {@link File} that represents the local file that {@link FilePath} has represented. - * @param channel - * The "back pointer" of the {@link Channel} that represents the communication - * with the node from where the code was sent. + * @param f {@link File} that represents the local file that + * {@link FilePath} has represented. + * @param channel The "back pointer" of the {@link Channel} that + * represents the communication with the node from where the code was + * sent. */ T invoke(File f, VirtualChannel channel) throws IOException, InterruptedException; } @@ -733,21 +766,21 @@ public final class FilePath implements Serializable { * so that one can perform local file operations. */ public <T> T act(final FileCallable<T> callable) throws IOException, InterruptedException { - return act(callable,callable.getClass().getClassLoader()); + return act(callable, callable.getClass().getClassLoader()); } private <T> T act(final FileCallable<T> callable, ClassLoader cl) throws IOException, InterruptedException { - if(channel!=null) { + if (channel != null) { // run this on a remote system try { - return channel.call(new FileCallableWrapper<T>(callable,cl)); + return channel.call(new FileCallableWrapper<T>(callable, cl)); } catch (TunneledInterruptedException e) { - throw (InterruptedException)new InterruptedException().initCause(e); + throw (InterruptedException) new InterruptedException().initCause(e); } catch (AbortException e) { throw e; // pass through so that the caller can catch it as AbortException } catch (IOException e) { // wrap it into a new IOException so that we get the caller's stack trace as well. - throw new IOException2("remote file operation failed: "+remote+" at "+channel,e); + throw new IOException2("remote file operation failed: " + remote + " at " + channel, e); } } else { // the file is on the local machine. @@ -761,11 +794,11 @@ public final class FilePath implements Serializable { */ public <T> Future<T> actAsync(final FileCallable<T> callable) throws IOException, InterruptedException { try { - return (channel!=null ? channel : Hudson.MasterComputer.localChannel) - .callAsync(new FileCallableWrapper<T>(callable)); + return (channel != null ? channel : Hudson.MasterComputer.localChannel) + .callAsync(new FileCallableWrapper<T>(callable)); } catch (IOException e) { // wrap it into a new IOException so that we get the caller's stack trace as well. - throw new IOException2("remote file operation failed",e); + throw new IOException2("remote file operation failed", e); } } @@ -773,8 +806,8 @@ public final class FilePath implements Serializable { * Executes some program on the machine that this {@link FilePath} exists, * so that one can perform local file operations. */ - public <V,E extends Throwable> V act(Callable<V,E> callable) throws IOException, InterruptedException, E { - if(channel!=null) { + public <V, E extends Throwable> V act(Callable<V, E> callable) throws IOException, InterruptedException, E { + if (channel != null) { // run this on a remote system return channel.call(callable); } else { @@ -784,8 +817,8 @@ public final class FilePath implements Serializable { } /** - * Converts this file to the URI, relative to the machine - * on which this file is available. + * Converts this file to the URI, relative to the machine on which this file + * is available. */ public URI toURI() throws IOException, InterruptedException { return act(new FileCallable<URI>() { @@ -799,18 +832,19 @@ public final class FilePath implements Serializable { * Creates this directory. */ public void mkdirs() throws IOException, InterruptedException { - if(!act(new FileCallable<Boolean>() { + if (!act(new FileCallable<Boolean>() { public Boolean invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { - if(f.mkdirs() || f.exists()) + if (f.mkdirs() || f.exists()) { return true; // OK - + } // following Ant <mkdir> task to avoid possible race condition. Thread.sleep(10); return f.mkdirs() || f.exists(); } - })) - throw new IOException("Failed to mkdirs: "+remote); + })) { + throw new IOException("Failed to mkdirs: " + remote); + } } /** @@ -845,32 +879,39 @@ public final class FilePath implements Serializable { public String getBaseName() { String n = getName(); int idx = n.lastIndexOf('.'); - if (idx<0) return n; - return n.substring(0,idx); + if (idx < 0) { + return n; + } + return n.substring(0, idx); } + /** * Gets just the file name portion. * - * This method assumes that the file name is the same between local and remote. + * This method assumes that the file name is the same between local and + * remote. */ public String getName() { String r = remote; - if(r.endsWith("\\") || r.endsWith("/")) - r = r.substring(0,r.length()-1); + if (r.endsWith("\\") || r.endsWith("/")) { + r = r.substring(0, r.length() - 1); + } - int len = r.length()-1; - while(len>=0) { + int len = r.length() - 1; + while (len >= 0) { char ch = r.charAt(len); - if(ch=='\\' || ch=='/') + if (ch == '\\' || ch == '/') { break; + } len--; } - return r.substring(len+1); + return r.substring(len + 1); } /** - * Short for {@code getParent().child(rel)}. Useful for getting other files in the same directory. + * Short for {@code getParent().child(rel)}. Useful for getting other files + * in the same directory. */ public FilePath sibling(String rel) { return getParent().child(rel); @@ -880,46 +921,50 @@ public final class FilePath implements Serializable { * Returns a {@link FilePath} by adding the given suffix to this path name. */ public FilePath withSuffix(String suffix) { - return new FilePath(channel,remote+suffix); + return new FilePath(channel, remote + suffix); } /** * The same as {@link FilePath#FilePath(FilePath,String)} but more OO. + * * @param rel a relative or absolute path * @return a file on the same channel */ public FilePath child(String rel) { - return new FilePath(this,rel); + return new FilePath(this, rel); } /** * Gets the parent file. + * * @return parent FilePath or null if there is no parent */ public FilePath getParent() { int i = remote.length() - 2; for (; i >= 0; i--) { char ch = remote.charAt(i); - if(ch=='\\' || ch=='/') + if (ch == '\\' || ch == '/') { break; + } } - return i >= 0 ? new FilePath( channel, remote.substring(0,i+1) ) : null; + return i >= 0 ? new FilePath(channel, remote.substring(0, i + 1)) : null; } /** - * Creates a temporary file in the directory that this {@link FilePath} object designates. + * Creates a temporary file in the directory that this {@link FilePath} + * object designates. */ public FilePath createTempFile(final String prefix, final String suffix) throws IOException, InterruptedException { try { - return new FilePath(this,act(new FileCallable<String>() { + return new FilePath(this, act(new FileCallable<String>() { public String invoke(File dir, VirtualChannel channel) throws IOException { File f = File.createTempFile(prefix, suffix, dir); return f.getName(); } })); } catch (IOException e) { - throw new IOException2("Failed to create a temp file on "+remote,e); + throw new IOException2("Failed to create a temp file on " + remote, e); } } @@ -928,7 +973,7 @@ public final class FilePath implements Serializable { * given text (encoded in the platform default encoding) */ public FilePath createTextTempFile(final String prefix, final String suffix, final String contents) throws IOException, InterruptedException { - return createTextTempFile(prefix,suffix,contents,true); + return createTextTempFile(prefix, suffix, contents, true); } /** @@ -937,18 +982,19 @@ public final class FilePath implements Serializable { */ public FilePath createTextTempFile(final String prefix, final String suffix, final String contents, final boolean inThisDirectory) throws IOException, InterruptedException { try { - return new FilePath(channel,act(new FileCallable<String>() { + return new FilePath(channel, act(new FileCallable<String>() { public String invoke(File dir, VirtualChannel channel) throws IOException { - if(!inThisDirectory) + if (!inThisDirectory) { dir = new File(System.getProperty("java.io.tmpdir")); - else + } else { dir.mkdirs(); + } File f; try { f = File.createTempFile(prefix, suffix, dir); } catch (IOException e) { - throw new IOException2("Failed to create a temporary directory in "+dir,e); + throw new IOException2("Failed to create a temporary directory in " + dir, e); } Writer w = null; try { @@ -962,17 +1008,18 @@ public final class FilePath implements Serializable { } })); } catch (IOException e) { - throw new IOException2("Failed to create a temp file on "+remote,e); + throw new IOException2("Failed to create a temp file on " + remote, e); } } /** * Creates a temporary directory inside the directory represented by 'this' + * * @since 1.311 */ public FilePath createTempDir(final String prefix, final String suffix) throws IOException, InterruptedException { try { - return new FilePath(this,act(new FileCallable<String>() { + return new FilePath(this, act(new FileCallable<String>() { public String invoke(File dir, VirtualChannel channel) throws IOException { File f = File.createTempFile(prefix, suffix, dir); f.delete(); @@ -981,12 +1028,13 @@ public final class FilePath implements Serializable { } })); } catch (IOException e) { - throw new IOException2("Failed to create a temp directory on "+remote,e); + throw new IOException2("Failed to create a temp directory on " + remote, e); } } /** * Deletes this file. + * * @throws IOException if it exists but could not be successfully deleted * @return true, for a modicum of compatibility */ @@ -1012,8 +1060,8 @@ public final class FilePath implements Serializable { } /** - * Gets the last modified time stamp of this file, by using the clock - * of the machine where this file actually resides. + * Gets the last modified time stamp of this file, by using the clock of the + * machine where this file actually resides. * * @see File#lastModified() * @see #touch(long) @@ -1034,10 +1082,12 @@ public final class FilePath implements Serializable { public void touch(final long timestamp) throws IOException, InterruptedException { act(new FileCallable<Void>() { public Void invoke(File f, VirtualChannel channel) throws IOException { - if(!f.exists()) + if (!f.exists()) { new FileOutputStream(f).close(); - if(!f.setLastModified(timestamp)) - throw new IOException("Failed to set the timestamp of "+f+" to "+timestamp); + } + if (!f.setLastModified(timestamp)) { + throw new IOException("Failed to set the timestamp of " + f + " to " + timestamp); + } return null; } }); @@ -1053,7 +1103,7 @@ public final class FilePath implements Serializable { } }); } - + /** * Returns the file size in bytes. * @@ -1072,14 +1122,15 @@ public final class FilePath implements Serializable { * * On Windows, no-op. * - * @param mask - * File permission mask. To simplify the permission copying, - * if the parameter is -1, this method becomes no-op. + * @param mask File permission mask. To simplify the permission copying, if + * the parameter is -1, this method becomes no-op. * @since 1.303 * @see #mode() */ public void chmod(final int mask) throws IOException, InterruptedException { - if(!isUnix() || mask==-1) return; + if (!isUnix() || mask == -1) { + return; + } act(new FileCallable<Void>() { public Void invoke(File f, VirtualChannel channel) throws IOException { Util.chmod(f, mask); @@ -1091,13 +1142,14 @@ public final class FilePath implements Serializable { /** * Gets the file permission bit mask. * - * @return - * -1 on Windows, since such a concept doesn't make sense. + * @return -1 on Windows, since such a concept doesn't make sense. * @since 1.311 * @see #chmod(int) */ public int mode() throws IOException, InterruptedException { - if(!isUnix()) return -1; + if (!isUnix()) { + return -1; + } return act(new FileCallable<Integer>() { public Integer invoke(File f, VirtualChannel channel) throws IOException { int mode = -1; @@ -1106,7 +1158,7 @@ public final class FilePath implements Serializable { } catch (NativeAccessException ex) { LOGGER.log(Level.WARNING, "Native function mod failed ({0})", NativeUtils.getInstance().getLastUnixError()); } - return mode; + return mode; } }); } @@ -1114,11 +1166,11 @@ public final class FilePath implements Serializable { /** * List up files and directories in this directory. * - * <p> - * This method returns direct children of the directory denoted by the 'this' object. + * <p> This method returns direct children of the directory denoted by the + * 'this' object. */ public List<FilePath> list() throws IOException, InterruptedException { - return list((FileFilter)null); + return list((FileFilter) null); } /** @@ -1131,6 +1183,7 @@ public final class FilePath implements Serializable { } private static final class DirectoryFilter implements FileFilter, Serializable { + public boolean accept(File f) { return f.isDirectory(); } @@ -1138,13 +1191,13 @@ public final class FilePath implements Serializable { } /** - * List up files in this directory, just like {@link File#listFiles(FileFilter)}. + * List up files in this directory, just like + * {@link File#listFiles(FileFilter)}. * - * @param filter - * The optional filter used to narrow down the result. - * If non-null, must be {@link Serializable}. - * If this {@link FilePath} represents a remote path, - * the filter object will be executed on the remote machine. + * @param filter The optional filter used to narrow down the result. If + * non-null, must be {@link Serializable}. If this {@link FilePath} + * represents a remote path, the filter object will be executed on the + * remote machine. */ public List<FilePath> list(final FileFilter filter) throws IOException, InterruptedException { if (filter != null && !(filter instanceof Serializable)) { @@ -1153,33 +1206,36 @@ public final class FilePath implements Serializable { return act(new FileCallable<List<FilePath>>() { public List<FilePath> invoke(File f, VirtualChannel channel) throws IOException { File[] children = f.listFiles(filter); - if(children ==null) return null; + if (children == null) { + return null; + } ArrayList<FilePath> r = new ArrayList<FilePath>(children.length); - for (File child : children) + for (File child : children) { r.add(new FilePath(child)); + } return r; } - }, (filter!=null?filter:this).getClass().getClassLoader()); + }, (filter != null ? filter : this).getClass().getClassLoader()); } /** * List up files in this directory that matches the given Ant-style filter. * - * @param includes - * See {@link FileSet} for the syntax. String like "foo/*.zip" or "foo/**/*.xml" - * @return - * can be empty but always non-null. + * @param includes See {@link FileSet} for the syntax. String like + * "foo/*.zip" or "foo/**/*.xml" + * @return can be empty but always non-null. */ public FilePath[] list(final String includes) throws IOException, InterruptedException { return act(new FileCallable<FilePath[]>() { public FilePath[] invoke(File f, VirtualChannel channel) throws IOException { - String[] files = glob(f,includes); + String[] files = glob(f, includes); FilePath[] r = new FilePath[files.length]; - for( int i=0; i<r.length; i++ ) - r[i] = new FilePath(new File(f,files[i])); + for (int i = 0; i < r.length; i++) { + r[i] = new FilePath(new File(f, files[i])); + } return r; } @@ -1189,13 +1245,13 @@ public final class FilePath implements Serializable { /** * Runs Ant glob expansion. * - * @return - * A set of relative file names from the base directory. + * @return A set of relative file names from the base directory. */ private static String[] glob(File dir, String includes) throws IOException { - if(isAbsolute(includes)) - throw new IOException("Expecting Ant GLOB pattern, but saw '"+includes+"'. See http://ant.apache.org/manual/Types/fileset.html for syntax"); - FileSet fs = Util.createFileSet(dir,includes); + if (isAbsolute(includes)) { + throw new IOException("Expecting Ant GLOB pattern, but saw '" + includes + "'. See http://ant.apache.org/manual/Types/fileset.html for syntax"); + } + FileSet fs = Util.createFileSet(dir, includes); DirectoryScanner ds = fs.getDirectoryScanner(new Project()); String[] files = ds.getIncludedFiles(); return files; @@ -1205,16 +1261,17 @@ public final class FilePath implements Serializable { * Reads this file. */ public InputStream read() throws IOException { - if(channel==null) + if (channel == null) { return new FileInputStream(new File(remote)); + } final Pipe p = Pipe.createRemoteToLocal(); - channel.callAsync(new Callable<Void,IOException>() { + channel.callAsync(new Callable<Void, IOException>() { public Void call() throws IOException { - FileInputStream fis=null; + FileInputStream fis = null; try { fis = new FileInputStream(new File(remote)); - Util.copyStream(fis,p.getOut()); + Util.copyStream(fis, p.getOut()); return null; } finally { IOUtils.closeQuietly(fis); @@ -1239,18 +1296,17 @@ public final class FilePath implements Serializable { } /** - * Writes to this file. - * If this file already exists, it will be overwritten. + * Writes to this file. If this file already exists, it will be overwritten. * If the directory doesn't exist, it will be created. */ public OutputStream write() throws IOException, InterruptedException { - if(channel==null) { + if (channel == null) { File f = new File(remote).getAbsoluteFile(); f.getParentFile().mkdirs(); return new FileOutputStream(f); } - return channel.call(new Callable<OutputStream,IOException>() { + return channel.call(new Callable<OutputStream, IOException>() { public OutputStream call() throws IOException { File f = new File(remote).getAbsoluteFile(); f.getParentFile().mkdirs(); @@ -1263,8 +1319,7 @@ public final class FilePath implements Serializable { /** * Overwrites this file by placing the given String as the content. * - * @param encoding - * Null to use the platform default encoding. + * @param encoding Null to use the platform default encoding. * @since 1.105 */ public void write(final String content, final String encoding) throws IOException, InterruptedException { @@ -1295,38 +1350,40 @@ public final class FilePath implements Serializable { } /** - * Rename this file/directory to the target filepath. This FilePath and the target must - * be on the some host + * Rename this file/directory to the target filepath. This FilePath and the + * target must be on the some host */ public void renameTo(final FilePath target) throws IOException, InterruptedException { - if(this.channel != target.channel) { - throw new IOException("renameTo target must be on the same host"); - } + if (this.channel != target.channel) { + throw new IOException("renameTo target must be on the same host"); + } act(new FileCallable<Void>() { public Void invoke(File f, VirtualChannel channel) throws IOException { - f.renameTo(new File(target.remote)); + f.renameTo(new File(target.remote)); return null; } }); } /** - * Moves all the contents of this directory into the specified directory, then delete this directory itself. + * Moves all the contents of this directory into the specified directory, + * then delete this directory itself. * * @since 1.308. */ public void moveAllChildrenTo(final FilePath target) throws IOException, InterruptedException { - if(this.channel != target.channel) { + if (this.channel != target.channel) { throw new IOException("pullUpTo target must be on the same host"); } act(new FileCallable<Void>() { public Void invoke(File f, VirtualChannel channel) throws IOException { File t = new File(target.getRemote()); - - for(File child : f.listFiles()) { + + for (File child : f.listFiles()) { File target = new File(t, child.getName()); - if(!child.renameTo(target)) - throw new IOException("Failed to rename "+child+" to "+target); + if (!child.renameTo(target)) { + throw new IOException("Failed to rename " + child + " to " + target); + } } f.delete(); return null; @@ -1346,12 +1403,13 @@ public final class FilePath implements Serializable { out.close(); } } catch (IOException e) { - throw new IOException2("Failed to copy "+this+" to "+target,e); + throw new IOException2("Failed to copy " + this + " to " + target, e); } } /** * Copies this file to the specified target, with file permissions intact. + * * @since 1.311 */ public void copyToWithPermission(FilePath target) throws IOException, InterruptedException { @@ -1371,9 +1429,9 @@ public final class FilePath implements Serializable { FileInputStream fis = null; try { fis = new FileInputStream(f); - Util.copyStream(fis,out); + Util.copyStream(fis, out); try { - if (Channel.current() != null){ + if (Channel.current() != null) { Channel.current().flushPipe(); } } catch (InterruptedException ex) { @@ -1389,57 +1447,64 @@ public final class FilePath implements Serializable { } /** - * Remoting interface used for {@link FilePath#copyRecursiveTo(String, FilePath)}. + * Remoting interface used for + * {@link FilePath#copyRecursiveTo(String, FilePath)}. * * TODO: this might not be the most efficient way to do the copy. */ interface RemoteCopier { + /** - * @param fileName - * relative path name to the output file. Path separator must be '/'. + * @param fileName relative path name to the output file. Path separator + * must be '/'. */ void open(String fileName) throws IOException; + void write(byte[] buf, int len) throws IOException; + void close() throws IOException; } /** - * Copies the contents of this directory recursively into the specified target directory. - * @since 1.312 + * Copies the contents of this directory recursively into the specified + * target directory. + * + * @since 1.312 */ public int copyRecursiveTo(FilePath target) throws IOException, InterruptedException { - return copyRecursiveTo("**/*",target); + return copyRecursiveTo("**/*", target); } public int copyRecursiveTo(String fileMask, FilePath target) throws IOException, InterruptedException { - return copyRecursiveTo(fileMask,null,target); + return copyRecursiveTo(fileMask, null, target); } /** - * {@see copyRecursiveTo(String fileMask, String excludes, FilePath target, FilePath.TarCompression remoteCompressionType)} + * { + * + * @see copyRecursiveTo(String fileMask, String excludes, FilePath target, + * FilePath.TarCompression remoteCompressionType)} */ public int copyRecursiveTo(final String fileMask, final String excludes, final FilePath target) - throws IOException, InterruptedException { + throws IOException, InterruptedException { return copyRecursiveTo(fileMask, excludes, target, FilePath.TarCompression.GZIP); } /** - * Copies the files that match the given file mask to the specified target node. + * Copies the files that match the given file mask to the specified target + * node. * - * @param fileMask - * Ant GLOB pattern. - * String like "foo/bar/*.xml" Multiple patterns can be separated - * by ',', and whitespace can surround ',' (so that you can write - * "abc, def" and "abc,def" to mean the same thing. - * @param excludes - * Files to be excluded. Can be null. - * @param remoteCompressionType compression type which will be used before master<->slave files transfer. - * @return - * the number of files copied. + * @param fileMask Ant GLOB pattern. String like "foo/bar/*.xml" Multiple + * patterns can be separated by ',', and whitespace can surround ',' (so + * that you can write "abc, def" and "abc,def" to mean the same thing. + * @param excludes Files to be excluded. Can be null. + * @param remoteCompressionType compression type which will be used before + * master<->slave files transfer. + * @return the number of files copied. */ public int copyRecursiveTo(final String fileMask, final String excludes, final FilePath target, - final FilePath.TarCompression remoteCompressionType) - throws IOException, InterruptedException { + final FilePath.TarCompression remoteCompressionType) + throws IOException, InterruptedException { if (this.channel == target.channel) { // local to local copy. return act(new FileCallable<Integer>() { @@ -1451,6 +1516,7 @@ public final class FilePath implements Serializable { try { class CopyImpl extends Copy { + private int copySize; public CopyImpl() { @@ -1488,17 +1554,17 @@ public final class FilePath implements Serializable { Future<Void> future = target.actAsync(new FileCallable<Void>() { public Void invoke(File f, VirtualChannel channel) throws IOException { try { - readFromTar(remote + '/' + fileMask, f, (remoteCompressionType != null? - remoteCompressionType.extract(pipe.getIn()) : - FilePath.TarCompression.GZIP.extract(pipe.getIn()))); + readFromTar(remote + '/' + fileMask, f, (remoteCompressionType != null + ? remoteCompressionType.extract(pipe.getIn()) + : FilePath.TarCompression.GZIP.extract(pipe.getIn()))); return null; } finally { pipe.getIn().close(); } } }); - int r = writeToTar(new File(remote), fileMask, excludes, (remoteCompressionType != null? - remoteCompressionType.compress(pipe.getOut()) : FilePath.TarCompression.GZIP.compress(pipe.getOut()))); + int r = writeToTar(new File(remote), fileMask, excludes, (remoteCompressionType != null + ? remoteCompressionType.compress(pipe.getOut()) : FilePath.TarCompression.GZIP.compress(pipe.getOut()))); try { future.get(); } catch (ExecutionException e) { @@ -1512,9 +1578,9 @@ public final class FilePath implements Serializable { Future<Integer> future = actAsync(new FileCallable<Integer>() { public Integer invoke(File f, VirtualChannel channel) throws IOException { try { - return writeToTar(f, fileMask, excludes, (remoteCompressionType != null? - remoteCompressionType.compress(pipe.getOut()) : - FilePath.TarCompression.GZIP.compress(pipe.getOut()))); + return writeToTar(f, fileMask, excludes, (remoteCompressionType != null + ? remoteCompressionType.compress(pipe.getOut()) + : FilePath.TarCompression.GZIP.compress(pipe.getOut()))); } finally { pipe.getOut().close(); } @@ -1523,8 +1589,8 @@ public final class FilePath implements Serializable { try { //it's possible to get NPE if on slave works old process readFromTar(remote + '/' + fileMask, new File(target.remote), - (remoteCompressionType != null? remoteCompressionType.extract(pipe.getIn()) : - FilePath.TarCompression.GZIP.extract(pipe.getIn()))); + (remoteCompressionType != null ? remoteCompressionType.extract(pipe.getIn()) + : FilePath.TarCompression.GZIP.extract(pipe.getIn()))); } catch (IOException e) {// BuildException or IOException try { future.get(3, TimeUnit.SECONDS); @@ -1545,12 +1611,10 @@ public final class FilePath implements Serializable { } } - /** * Writes files in 'this' directory to a tar stream. * - * @param glob - * Ant file pattern mask, like "**/*.java". + * @param glob Ant file pattern mask, like "**/*.java". */ public int tar(OutputStream out, final String glob) throws IOException, InterruptedException { return archive(ArchiverFactory.TAR, out, glob); @@ -1561,7 +1625,8 @@ public final class FilePath implements Serializable { } /** - * Uses the given scanner on 'this' directory to list up files and then archive it to a tar stream. + * Uses the given scanner on 'this' directory to list up files and then + * archive it to a tar stream. */ public int tar(OutputStream out, DirScanner scanner) throws IOException, InterruptedException { return archive(ArchiverFactory.TAR, out, scanner); @@ -1570,13 +1635,12 @@ public final class FilePath implements Serializable { /** * Writes to a tar stream and stores obtained files to the base dir. * - * @return - * number of files/directories that are written. + * @return number of files/directories that are written. */ private static Integer writeToTar(File baseDir, String fileMask, String excludes, OutputStream out) throws IOException { Archiver tw = ArchiverFactory.TAR.create(out); try { - new DirScanner.Glob(fileMask,excludes).scan(baseDir,tw); + new DirScanner.Glob(fileMask, excludes).scan(baseDir, tw); } finally { tw.close(); } @@ -1591,79 +1655,88 @@ public final class FilePath implements Serializable { try { TarEntry te; while ((te = t.getNextEntry()) != null) { - File f = new File(baseDir,te.getName()); - if(te.isDirectory()) { + File f = new File(baseDir, te.getName()); + if (te.isDirectory()) { f.mkdirs(); } else { File parent = f.getParentFile(); - if (parent != null) parent.mkdirs(); + if (parent != null) { + parent.mkdirs(); + } - IOUtils.copy(t,f); + IOUtils.copy(t, f); f.setLastModified(te.getModTime().getTime()); - int mode = te.getMode()&0777; - if(mode!=0 && !Functions.isWindows()) // be defensive + int mode = te.getMode() & 0777; + if (mode != 0 && !Functions.isWindows()) // be defensive + { Util.chmod(f, mode); + } } } - } catch(IOException e) { - throw new IOException2("Failed to extract "+name,e); + } catch (IOException e) { + throw new IOException2("Failed to extract " + name, e); } finally { t.close(); } } /** - * Creates a {@link Launcher} for starting processes on the node - * that has this file. + * Creates a {@link Launcher} for starting processes on the node that has + * this file. + * * @since 1.89 */ public Launcher createLauncher(TaskListener listener) throws IOException, InterruptedException { - if(channel==null) + if (channel == null) { return new LocalLauncher(listener); - else - return new RemoteLauncher(listener,channel,channel.call(new IsUnix())); + } else { + return new RemoteLauncher(listener, channel, channel.call(new IsUnix())); + } } - private static final class IsUnix implements Callable<Boolean,IOException> { + private static final class IsUnix implements Callable<Boolean, IOException> { + public Boolean call() throws IOException { - return File.pathSeparatorChar==':'; + return File.pathSeparatorChar == ':'; } private static final long serialVersionUID = 1L; } /** - * Validates the ant file mask (like "foo/bar/*.txt, zot/*.jar") - * against this directory, and try to point out the problem. + * Validates the ant file mask (like "foo/bar/*.txt, zot/*.jar") against + * this directory, and try to point out the problem. * - * <p> - * This is useful in conjunction with {@link FormValidation}. + * <p> This is useful in conjunction with {@link FormValidation}. * - * @return - * null if no error was found. Otherwise returns a human readable error message. + * @return null if no error was found. Otherwise returns a human readable + * error message. * @since 1.90 * @see #validateFileMask(FilePath, String) */ public String validateAntFileMask(final String fileMasks) throws IOException, InterruptedException { return act(new FileCallable<String>() { public String invoke(File dir, VirtualChannel channel) throws IOException { - if(fileMasks.startsWith("~")) + if (fileMasks.startsWith("~")) { return Messages.FilePath_TildaDoesntWork(); + } - StringTokenizer tokens = new StringTokenizer(fileMasks,","); + StringTokenizer tokens = new StringTokenizer(fileMasks, ","); - while(tokens.hasMoreTokens()) { + while (tokens.hasMoreTokens()) { final String fileMask = tokens.nextToken().trim(); - if(hasMatch(dir,fileMask)) + if (hasMatch(dir, fileMask)) { continue; // no error on this portion - + } // in 1.172 we introduced an incompatible change to stop using ' ' as the separator // so see if we can match by using ' ' as the separator - if(fileMask.contains(" ")) { + if (fileMask.contains(" ")) { boolean matched = true; - for (String token : Util.tokenize(fileMask)) - matched &= hasMatch(dir,token); - if(matched) + for (String token : Util.tokenize(fileMask)) { + matched &= hasMatch(dir, token); + } + if (matched) { return Messages.FilePath_validateAntFileMask_whitespaceSeprator(); + } } // a common mistake is to assume the wrong base dir, and there are two variations @@ -1671,41 +1744,47 @@ public final class FilePath implements Serializable { // and (2) the user gave us cc/dd where aa/bb/cc/dd was correct. {// check the (1) above first - String f=fileMask; - while(true) { + String f = fileMask; + while (true) { int idx = findSeparator(f); - if(idx==-1) break; - f=f.substring(idx+1); + if (idx == -1) { + break; + } + f = f.substring(idx + 1); - if(hasMatch(dir,f)) - return Messages.FilePath_validateAntFileMask_doesntMatchAndSuggest(fileMask,f); + if (hasMatch(dir, f)) { + return Messages.FilePath_validateAntFileMask_doesntMatchAndSuggest(fileMask, f); + } } } {// check the (2) above next as this is more expensive. // Try prepending "**/" to see if that results in a match - FileSet fs = Util.createFileSet(dir,"**/"+fileMask); + FileSet fs = Util.createFileSet(dir, "**/" + fileMask); DirectoryScanner ds = fs.getDirectoryScanner(new Project()); - if(ds.getIncludedFilesCount()!=0) { + if (ds.getIncludedFilesCount() != 0) { // try shorter name first so that the suggestion results in least amount of changes String[] names = ds.getIncludedFiles(); - Arrays.sort(names,SHORTER_STRING_FIRST); - for( String f : names) { + Arrays.sort(names, SHORTER_STRING_FIRST); + for (String f : names) { // now we want to decompose f to the leading portion that matched "**" // and the trailing portion that matched the file mask, so that // we can suggest the user error. // // this is not a very efficient/clever way to do it, but it's relatively simple - String prefix=""; - while(true) { + String prefix = ""; + while (true) { int idx = findSeparator(f); - if(idx==-1) break; - - prefix+=f.substring(0,idx)+'/'; - f=f.substring(idx+1); - if(hasMatch(dir,prefix+fileMask)) - return Messages.FilePath_validateAntFileMask_doesntMatchAndSuggest(fileMask, prefix+fileMask); + if (idx == -1) { + break; + } + + prefix += f.substring(0, idx) + '/'; + f = f.substring(idx + 1); + if (hasMatch(dir, prefix + fileMask)) { + return Messages.FilePath_validateAntFileMask_doesntMatchAndSuggest(fileMask, prefix + fileMask); + } } } } @@ -1715,26 +1794,28 @@ public final class FilePath implements Serializable { String previous = null; String pattern = fileMask; - while(true) { - if(hasMatch(dir,pattern)) { + while (true) { + if (hasMatch(dir, pattern)) { // found a match - if(previous==null) - return Messages.FilePath_validateAntFileMask_portionMatchAndSuggest(fileMask,pattern); - else - return Messages.FilePath_validateAntFileMask_portionMatchButPreviousNotMatchAndSuggest(fileMask,pattern,previous); + if (previous == null) { + return Messages.FilePath_validateAntFileMask_portionMatchAndSuggest(fileMask, pattern); + } else { + return Messages.FilePath_validateAntFileMask_portionMatchButPreviousNotMatchAndSuggest(fileMask, pattern, previous); + } } int idx = findSeparator(pattern); - if(idx<0) {// no more path component left to go back - if(pattern.equals(fileMask)) + if (idx < 0) {// no more path component left to go back + if (pattern.equals(fileMask)) { return Messages.FilePath_validateAntFileMask_doesntMatchAnything(fileMask); - else - return Messages.FilePath_validateAntFileMask_doesntMatchAnythingAndSuggest(fileMask,pattern); + } else { + return Messages.FilePath_validateAntFileMask_doesntMatchAnythingAndSuggest(fileMask, pattern); + } } // cut off the trailing component and try again previous = pattern; - pattern = pattern.substring(0,idx); + pattern = pattern.substring(0, idx); } } } @@ -1743,10 +1824,10 @@ public final class FilePath implements Serializable { } private boolean hasMatch(File dir, String pattern) { - FileSet fs = Util.createFileSet(dir,pattern); + FileSet fs = Util.createFileSet(dir, pattern); DirectoryScanner ds = fs.getDirectoryScanner(new Project()); - return ds.getIncludedFilesCount()!=0 || ds.getIncludedDirsCount()!=0; + return ds.getIncludedFilesCount() != 0 || ds.getIncludedDirsCount() != 0; } /** @@ -1755,31 +1836,40 @@ public final class FilePath implements Serializable { private int findSeparator(String pattern) { int idx1 = pattern.indexOf('\\'); int idx2 = pattern.indexOf('/'); - if(idx1==-1) return idx2; - if(idx2==-1) return idx1; - return Math.min(idx1,idx2); + if (idx1 == -1) { + return idx2; + } + if (idx2 == -1) { + return idx1; + } + return Math.min(idx1, idx2); } }); } /** - * Shortcut for {@link #validateFileMask(String)} in case the left-hand side can be null. + * Shortcut for {@link #validateFileMask(String)} in case the left-hand side + * can be null. */ public static FormValidation validateFileMask(FilePath pathOrNull, String value) throws IOException { - if(pathOrNull==null) return FormValidation.ok(); + if (pathOrNull == null) { + return FormValidation.ok(); + } return pathOrNull.validateFileMask(value); } /** - * Short for {@code validateFileMask(value,true)} + * Short for {@code validateFileMask(value,true)} */ public FormValidation validateFileMask(String value) throws IOException { - return validateFileMask(value,true); + return validateFileMask(value, true); } /** - * Checks the GLOB-style file mask. See {@link #validateAntFileMask(String)}. - * Requires configure permission on ancestor AbstractProject object in request. + * Checks the GLOB-style file mask. See + * {@link #validateAntFileMask(String)}. Requires configure permission on + * ancestor AbstractProject object in request. + * * @since 1.294 */ public FormValidation validateFileMask(String value, boolean errorIfNotExist) throws IOException { @@ -1787,139 +1877,153 @@ public final class FilePath implements Serializable { subject.checkPermission(Item.CONFIGURE); value = fixEmpty(value); - if(value==null) + if (value == null) { return FormValidation.ok(); + } try { - if(!exists()) // no workspace. can't check + if (!exists()) // no workspace. can't check + { return FormValidation.ok(); + } String msg = validateAntFileMask(value); - if(errorIfNotExist) return FormValidation.error(msg); - else return FormValidation.warning(msg); + if (errorIfNotExist) { + return FormValidation.error(msg); + } else { + return FormValidation.warning(msg); + } } catch (InterruptedException e) { return FormValidation.ok(); } } /** - * Validates a relative file path from this {@link FilePath}. - * Requires configure permission on ancestor AbstractProject object in request. + * Validates a relative file path from this {@link FilePath}. Requires + * configure permission on ancestor AbstractProject object in request. * - * @param value - * The relative path being validated. - * @param errorIfNotExist - * If true, report an error if the given relative path doesn't exist. Otherwise it's a warning. - * @param expectingFile - * If true, we expect the relative path to point to a file. - * Otherwise, the relative path is expected to be pointing to a directory. + * @param value The relative path being validated. + * @param errorIfNotExist If true, report an error if the given relative + * path doesn't exist. Otherwise it's a warning. + * @param expectingFile If true, we expect the relative path to point to a + * file. Otherwise, the relative path is expected to be pointing to a + * directory. */ public FormValidation validateRelativePath(String value, boolean errorIfNotExist, boolean expectingFile) throws IOException { - AbstractProject subject = Stapler.getCurrentRequest().findAncestorObject(AbstractProject.class); + AbstractProject subject = Stapler.getCurrentRequest().findAncestorObject(AbstractProject.class); value = fixEmpty(value); // none entered yet, or something is seriously wrong - if (value == null || (AbstractProject<?,?>) subject == null) { + if (value == null || (AbstractProject<?, ?>) subject == null) { return FormValidation.ok(); } subject.checkPermission(Item.CONFIGURE); // a common mistake is to use wildcard - if (value.contains("*")) { + if (value.contains("*")) { return FormValidation.error(Messages.FilePath_validateRelativePath_wildcardNotAllowed()); } try { // no base directory. can't check - if (!exists()) { + if (!exists()) { return FormValidation.ok(); } - + FilePath path = child(value); if (path.exists()) { if (expectingFile) { - if (!path.isDirectory()) + if (!path.isDirectory()) { return FormValidation.ok(); - else + } else { return FormValidation.error(Messages.FilePath_validateRelativePath_notFile(value)); + } } else { - if (path.isDirectory()) + if (path.isDirectory()) { return FormValidation.ok(); - else + } else { return FormValidation.error(Messages.FilePath_validateRelativePath_notDirectory(value)); + } } } - String msg = expectingFile ? Messages.FilePath_validateRelativePath_noSuchFile(value) : - Messages.FilePath_validateRelativePath_noSuchDirectory(value); - if (errorIfNotExist) return FormValidation.error(msg); - else return FormValidation.warning(msg); + String msg = expectingFile ? Messages.FilePath_validateRelativePath_noSuchFile(value) + : Messages.FilePath_validateRelativePath_noSuchDirectory(value); + if (errorIfNotExist) { + return FormValidation.error(msg); + } else { + return FormValidation.warning(msg); + } } catch (InterruptedException e) { return FormValidation.ok(); } } /** - * A convenience method over {@link #validateRelativePath(String, boolean, boolean)}. + * A convenience method over + * {@link #validateRelativePath(String, boolean, boolean)}. */ public FormValidation validateRelativeDirectory(String value, boolean errorIfNotExist) throws IOException { - return validateRelativePath(value,errorIfNotExist,false); + return validateRelativePath(value, errorIfNotExist, false); } public FormValidation validateRelativeDirectory(String value) throws IOException { - return validateRelativeDirectory(value,true); + return validateRelativeDirectory(value, true); } - @Deprecated @Override + @Deprecated + @Override public String toString() { // to make writing JSPs easily, return local return remote; } public VirtualChannel getChannel() { - if(channel!=null) return channel; - else return Hudson.MasterComputer.localChannel; + if (channel != null) { + return channel; + } else { + return Hudson.MasterComputer.localChannel; + } } /** - * Returns true if this {@link FilePath} represents a remote file. + * Returns true if this {@link FilePath} represents a remote file. */ public boolean isRemote() { - return channel!=null; + return channel != null; } private void writeObject(ObjectOutputStream oos) throws IOException { Channel target = Channel.current(); - if(channel!=null && channel!=target) + if (channel != null && channel != target) { throw new IllegalStateException("Can't send a remote FilePath to a different remote channel"); + } oos.defaultWriteObject(); - oos.writeBoolean(channel==null); + oos.writeBoolean(channel == null); } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { Channel channel = Channel.current(); - assert channel!=null; + assert channel != null; ois.defaultReadObject(); - if(ois.readBoolean()) { + if (ois.readBoolean()) { this.channel = channel; } else { this.channel = null; } } - private static final long serialVersionUID = 1L; - public static int SIDE_BUFFER_SIZE = 1024; - private static final Logger LOGGER = Logger.getLogger(FilePath.class.getName()); /** * Adapts {@link FileCallable} to {@link Callable}. */ - private class FileCallableWrapper<T> implements DelegatingCallable<T,IOException> { + private class FileCallableWrapper<T> implements DelegatingCallable<T, IOException> { + private final FileCallable<T> callable; private transient ClassLoader classLoader; @@ -1944,23 +2048,23 @@ public final class FilePath implements Serializable { public ClassLoader getClassLoader() { return classLoader; } - private static final long serialVersionUID = 1L; } /** - * Used to tunnel {@link InterruptedException} over a Java signature that only allows {@link IOException} + * Used to tunnel {@link InterruptedException} over a Java signature that + * only allows {@link IOException} */ private static class TunneledInterruptedException extends IOException2 { + private TunneledInterruptedException(InterruptedException cause) { super(cause); } private static final long serialVersionUID = 1L; } - private static final Comparator<String> SHORTER_STRING_FIRST = new Comparator<String>() { public int compare(String o1, String o2) { - return o1.length()-o2.length(); + return o1.length() - o2.length(); } }; } diff --git a/hudson-core/src/main/java/hudson/Functions.java b/hudson-core/src/main/java/hudson/Functions.java index 6918b7c..eda47ed 100644 --- a/hudson-core/src/main/java/hudson/Functions.java +++ b/hudson-core/src/main/java/hudson/Functions.java @@ -7,10 +7,10 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * - * Contributors: + * Contributors: * * Kohsuke Kawaguchi, Winston Prakash, Stephen Connolly, Tom Huybrechts, Alan Harder, Romain Seguy - * + * * *******************************************************************************/ @@ -125,13 +125,13 @@ import org.eclipse.hudson.security.HudsonSecurityManager; /** * Utility functions used in views. * - * <p> - * An instance of this class is created for each request and made accessible + * <p> An instance of this class is created for each request and made accessible * from view pages via the variable 'h' (h stands for Hudson.) * * @author Kohsuke Kawaguchi */ public class Functions { + private static volatile int globalIota = 0; private int iota; @@ -139,14 +139,14 @@ public class Functions { iota = globalIota; // concurrent requests can use the same ID --- we are just trying to // prevent the same user from seeing the same ID repeatedly. - globalIota+=1000; + globalIota += 1000; } /** * Generates an unique ID. */ public String generateId() { - return "id"+iota++; + return "id" + iota++; } public static boolean isModel(Object o) { @@ -162,23 +162,22 @@ public class Functions { } /** - * Given {@code c=MyList (extends ArrayList<Foo>), base=List}, compute the parameterization of 'base' - * that's assignable from 'c' (in this case {@code List<Foo>}), and return its n-th type parameter - * (n=0 would return {@code Foo}). + * Given {@code c=MyList (extends ArrayList<Foo>), base=List}, compute the + * parameterization of 'base' that's assignable from 'c' (in this case + * {@code List<Foo>}), and return its n-th type parameter (n=0 would return + * {@code Foo}). * - * <p> - * This method is useful for doing type arithmetic. + * <p> This method is useful for doing type arithmetic. * - * @throws AssertionError - * if c' is not parameterized. + * @throws AssertionError if c' is not parameterized. */ public static <B> Class getTypeParameter(Class<? extends B> c, Class<B> base, int n) { - Type parameterization = Types.getBaseClass(c,base); + Type parameterization = Types.getBaseClass(c, base); if (parameterization instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) parameterization; - return Types.erasure(Types.getTypeArgument(pt,n)); + return Types.erasure(Types.getTypeArgument(pt, n)); } else { - throw new AssertionError(c+" doesn't properly parameterize "+base); + throw new AssertionError(c + " doesn't properly parameterize " + base); } } @@ -187,24 +186,34 @@ public class Functions { } /** - * Prints the integer as a string that represents difference, - * like "-5", "+/-0", "+3". + * Prints the integer as a string that represents difference, like "-5", + * "+/-0", "+3". */ public static String getDiffString(int i) { - if(i==0) return "\u00B10"; // +/-0 + if (i == 0) { + return "\u00B10"; // +/-0 + } String s = Integer.toString(i); - if(i>0) return "+"+s; - else return s; + if (i > 0) { + return "+" + s; + } else { + return s; + } } /** * {@link #getDiffString(int)} that doesn't show anything for +/-0 */ public static String getDiffString2(int i) { - if(i==0) return ""; + if (i == 0) { + return ""; + } String s = Integer.toString(i); - if(i>0) return "+"+s; - else return s; + if (i > 0) { + return "+" + s; + } else { + return s; + } } /** @@ -212,10 +221,15 @@ public class Functions { * if there's something to print */ public static String getDiffString2(String prefix, int i, String suffix) { - if(i==0) return ""; + if (i == 0) { + return ""; + } String s = Integer.toString(i); - if(i>0) return prefix+"+"+s+suffix; - else return prefix+s+suffix; + if (i > 0) { + return prefix + "+" + s + suffix; + } else { + return prefix + s + suffix; + } } /** @@ -224,10 +238,11 @@ public class Functions { public static String addSuffix(int n, String singular, String plural) { StringBuilder buf = new StringBuilder(); buf.append(n).append(' '); - if(n==1) + if (n == 1) { buf.append(singular); - else + } else { buf.append(plural); + } return buf.toString(); } @@ -235,16 +250,19 @@ public class Functions { List<Ancestor> ancestors = req.getAncestors(); // find the first and last Run instances - Ancestor f=null,l=null; + Ancestor f = null, l = null; for (Ancestor anc : ancestors) { - if(anc.getObject() instanceof Run) { - if(f==null) f=anc; - l=anc; + if (anc.getObject() instanceof Run) { + if (f == null) { + f = anc; + } + l = anc; } } - if(l==null) return null; // there was no Run object - - String head = f.getPrev().getUrl()+'/'; + if (l == null) { + return null; // there was no Run object + } + String head = f.getPrev().getUrl() + '/'; String base = l.getUrl(); String reqUri = req.getOriginalRequestURI(); @@ -257,53 +275,52 @@ public class Functions { String furl = f.getUrl(); int slashCount = 0; // Count components in ancestor URL - for (int i = furl.indexOf('/'); i >= 0; i = furl.indexOf('/', i + 1)) slashCount++; + for (int i = furl.indexOf('/'); i >= 0; i = furl.indexOf('/', i + 1)) { + slashCount++; + } // Remove that many from request URL, ignoring extra slashes String rest = reqUri.replaceFirst("(?:/+[^/]*){" + slashCount + "}", ""); - return new RunUrl( (Run) f.getObject(), head, base, rest); + return new RunUrl((Run) f.getObject(), head, base, rest); } /** * If we know the user's screen resolution, return it. Otherwise null. + * * @since 1.213 */ public static Area getScreenResolution() { - Cookie res = Functions.getCookie(Stapler.getCurrentRequest(),"screenResolution"); - if(res!=null) + Cookie res = Functions.getCookie(Stapler.getCurrentRequest(), "screenResolution"); + if (res != null) { return Area.parse(res.getValue()); + } return null; } /** * URL decomposed for easier computation of relevant URLs. * - * <p> - * The decomposed URL will be of the form: + * <p> The decomposed URL will be of the form: * <pre> * aaaaaa/524/bbbbb/cccc * -head-| N |---rest--- * ----- base -----| * </pre> * - * <p> - * The head portion is the part of the URL from the {@link Hudson} - * object to the first {@link Run} subtype. When "next/prev build" - * is chosen, this part remains intact. + * <p> The head portion is the part of the URL from the {@link Hudson} + * object to the first {@link Run} subtype. When "next/prev build" is + * chosen, this part remains intact. * - * <p> - * The <tt>524</tt> is the path from {@link Job} to {@link Run}. + * <p> The <tt>524</tt> is the path from {@link Job} to {@link Run}. * - * <p> - * The <tt>bbb</tt> portion is the path after that till the last - * {@link Run} subtype. The <tt>ccc</tt> portion is the part - * after that. + * <p> The <tt>bbb</tt> portion is the path after that till the last + * {@link Run} subtype. The <tt>ccc</tt> portion is the part after that. */ public static final class RunUrl { + private final String head, base, rest; private final Run run; - public RunUrl(Run run, String head, String base, String rest) { this.run = run; this.head = head; @@ -330,10 +347,10 @@ public class Functions { } private String getUrl(Run n) { - if(n ==null) + if (n == null) { return null; - else { - return head+n.getNumber()+rest; + } else { + return head + n.getNumber() + rest; } } } @@ -347,9 +364,8 @@ public class Functions { } /** - * @deprecated as of 1.294 - * JEXL now supports the real ternary operator "x?y:z", so this work around - * is no longer necessary. + * @deprecated as of 1.294 JEXL now supports the real ternary operator + * "x?y:z", so this work around is no longer necessary. */ public static Object ifThenElse(boolean cond, Object thenValue, Object elseValue) { return cond ? thenValue : elseValue; @@ -360,15 +376,15 @@ public class Functions { } public static Map getSystemProperties() { - return new TreeMap<Object,Object>(System.getProperties()); + return new TreeMap<Object, Object>(System.getProperties()); } public static Map getEnvVars() { - return new TreeMap<String,String>(EnvVars.masterEnvVars); + return new TreeMap<String, String>(EnvVars.masterEnvVars); } public static boolean isWindows() { - return File.pathSeparatorChar==';'; + return File.pathSeparatorChar == ';'; } public static List<LogRecord> getLogRecords() { @@ -379,11 +395,11 @@ public class Functions { return formatter.format(r); } - public static Cookie getCookie(HttpServletRequest req,String name) { + public static Cookie getCookie(HttpServletRequest req, String name) { Cookie[] cookies = req.getCookies(); - if(cookies!=null) { + if (cookies != null) { for (Cookie cookie : cookies) { - if(cookie.getName().equals(name)) { + if (cookie.getName().equals(name)) { return cookie; } } @@ -391,9 +407,11 @@ public class Functions { return null; } - public static String getCookie(HttpServletRequest req,String name, String defaultValue) { + public static String getCookie(HttpServletRequest req, String name, String defaultValue) { Cookie c = getCookie(req, name); - if(c==null || c.getValue()==null) return defaultValue; + if (c == null || c.getValue() == null) { + return defaultValue; + } return c.getValue(); } @@ -403,7 +421,6 @@ public class Functions { public static String getYuiSuffix() { return DEBUG_YUI ? "debug" : "min"; } - /** * Set to true if you need to use the debug version of YUI. */ @@ -412,28 +429,32 @@ public class Functions { /** * Creates a sub map by using the given range (both ends inclusive). */ - public static <V> SortedMap<Integer,V> filter(SortedMap<Integer,V> map, String from, String to) { - if(from==null && to==null) return map; - if(to==null) - return map.headMap(Integer.parseInt(from)-1); - if(from==null) + public static <V> SortedMap<Integer, V> filter(SortedMap<Integer, V> map, String from, String to) { + if (from == null && to == null) { + return map; + } + if (to == null) { + return map.headMap(Integer.parseInt(from) - 1); + } + if (from == null) { return map.tailMap(Integer.parseInt(to)); + } - return map.subMap(Integer.parseInt(to),Integer.parseInt(from)-1); + return map.subMap(Integer.parseInt(to), Integer.parseInt(from) - 1); } - private static final SimpleFormatter formatter = new SimpleFormatter(); /** * Used by <tt>layout.jelly</tt> to control the auto refresh behavior. * - * @param noAutoRefresh - * On certain pages, like a page with forms, will have annoying interference - * with auto refresh. On those pages, disable auto-refresh. + * @param noAutoRefresh On certain pages, like a page with forms, will have + * annoying interference with auto refresh. On those pages, disable + * auto-refresh. */ public static void configureAutoRefresh(HttpServletRequest request, HttpServletResponse response, boolean noAutoRefresh) { - if(noAutoRefresh) + if (noAutoRefresh) { return; + } String param = request.getParameter("auto_refresh"); boolean refresh = isAutoRefresh(request); @@ -444,7 +465,7 @@ public class Functions { // Using request.getContextPath() might work but it seems simpler to just use the hudson_ prefix // to avoid conflicts with any other web apps that might be on the same machine. c.setPath("/"); - c.setMaxAge(60*60*24*30); // persist it roughly for a month + c.setMaxAge(60 * 60 * 24 * 30); // persist it roughly for a month response.addCookie(c); } if (refresh) { @@ -458,9 +479,9 @@ public class Functions { return Boolean.parseBoolean(param); } Cookie[] cookies = request.getCookies(); - if(cookies==null) + if (cookies == null) { return false; // when API design messes it up, we all suffer - + } for (Cookie c : cookies) { if (c.getName().equals("hudson_auto_refresh")) { return Boolean.parseBoolean(c.getValue()); @@ -470,16 +491,17 @@ public class Functions { } /** - * Finds the given object in the ancestor list and returns its URL. - * This is used to determine the "current" URL assigned to the given object, - * so that one can compute relative URLs from it. + * Finds the given object in the ancestor list and returns its URL. This is + * used to determine the "current" URL assigned to the given object, so that + * one can compute relative URLs from it. */ - public static String getNearestAncestorUrl(StaplerRequest req,Object it) { + public static String getNearestAncestorUrl(StaplerRequest req, Object it) { List list = req.getAncestors(); - for( int i=list.size()-1; i>=0; i-- ) { + for (int i = list.size() - 1; i >= 0; i--) { Ancestor anc = (Ancestor) list.get(i); - if(anc.getObject()==it) + if (anc.getObject() == it) { return anc.getUrl(); + } } return null; } @@ -489,29 +511,34 @@ public class Functions { */ public static String getSearchURL() { List list = Stapler.getCurrentRequest().getAncestors(); - for( int i=list.size()-1; i>=0; i-- ) { + for (int i = list.size() - 1; i >= 0; i--) { Ancestor anc = (Ancestor) list.get(i); - if(anc.getObject() instanceof SearchableModelObject) - return anc.getUrl()+"/search/"; + if (anc.getObject() instanceof SearchableModelObject) { + return anc.getUrl() + "/search/"; + } } return null; } public static String appendSpaceIfNotNull(String n) { - if(n==null) return null; - else return n+' '; + if (n == null) { + return null; + } else { + return n + ' '; + } } /** - * One nbsp per 10 pixels in given size, which may be a plain number or "NxN" - * (like an iconSize). Useful in a sortable table heading. + * One nbsp per 10 pixels in given size, which may be a plain number or + * "NxN" (like an iconSize). Useful in a sortable table heading. */ public static String nbspIndent(String size) { int i = size.indexOf('x'); i = Integer.parseInt(i > 0 ? size.substring(0, i) : size) / 10; StringBuilder buf = new StringBuilder(30); - for (int j = 0; j < i; j++) + for (int j = 0; j < i; j++) { buf.append(" "); + } return buf.toString(); } @@ -520,8 +547,10 @@ public class Functions { } public static boolean isMultiline(String s) { - if(s==null) return false; - return s.indexOf('\r')>=0 || s.indexOf('\n')>=0; + if (s == null) { + return false; + } + return s.indexOf('\r') >= 0 || s.indexOf('\n') >= 0; } public static String encode(String s) { @@ -537,11 +566,11 @@ public class Functions { } public static String xmlUnescape(String s) { - return s.replace("<","<").replace(">",">").replace("&","&"); + return s.replace("<", "<").replace(">", ">").replace("&", "&"); } public static void checkPermission(Permission permission) throws IOException, ServletException { - checkPermission(Hudson.getInstance(),permission); + checkPermission(Hudson.getInstance(), permission); } public static void checkPermission(AccessControlled object, Permission permission) throws IOException, ServletException { @@ -556,49 +585,51 @@ public class Functions { * Otherwise it will perform no check and that problem is hard to notice. */ public static void checkPermission(Object object, Permission permission) throws IOException, ServletException { - if (permission == null) + if (permission == null) { return; - - if (object instanceof AccessControlled) - checkPermission((AccessControlled) object,permission); - else { + } + + if (object instanceof AccessControlled) { + checkPermission((AccessControlled) object, permission); + } else { List<Ancestor> ancs = Stapler.getCurrentRequest().getAncestors(); - for(Ancestor anc : Iterators.reverse(ancs)) { + for (Ancestor anc : Iterators.reverse(ancs)) { Object o = anc.getObject(); if (o instanceof AccessControlled) { - checkPermission((AccessControlled) o,permission); + checkPermission((AccessControlled) o, permission); return; } } - checkPermission(Hudson.getInstance(),permission); + checkPermission(Hudson.getInstance(), permission); } } /** * Returns true if the current user has the given permission. * - * @param permission - * If null, returns true. This defaulting is convenient in making the use of this method terse. + * @param permission If null, returns true. This defaulting is convenient in + * making the use of this method terse. */ public static boolean hasPermission(Permission permission) throws IOException, ServletException { - return hasPermission(Hudson.getInstance(),permission); + return hasPermission(Hudson.getInstance(), permission); } /** - * This version is so that the 'hasPermission' can degrade gracefully - * if "it" is not an {@link AccessControlled} object. + * This version is so that the 'hasPermission' can degrade gracefully if + * "it" is not an {@link AccessControlled} object. */ public static boolean hasPermission(Object object, Permission permission) throws IOException, ServletException { - if (permission == null) + if (permission == null) { return true; - if (object instanceof AccessControlled) - return ((AccessControlled)object).hasPermission(permission); - else { + } + if (object instanceof AccessControlled) { + return ((AccessControlled) object).hasPermission(permission); + } else { List<Ancestor> ancs = Stapler.getCurrentRequest().getAncestors(); - for(Ancestor anc : Iterators.reverse(ancs)) { + for (Ancestor anc : Iterators.reverse(ancs)) { Object o = anc.getObject(); if (o instanceof AccessControlled) { - return ((AccessControlled)o).hasPermission(permission); + return ((AccessControlled) o).hasPermission(permission); } } return Hudson.getInstance().hasPermission(permission); @@ -608,7 +639,7 @@ public class Functions { public static void adminCheck(StaplerRequest req, StaplerResponse rsp, Object required, Permission permission) throws IOException, ServletException { // this is legacy --- all views should be eventually converted to // the permission based model. - if(required!=null && !Hudson.adminCheck(req,rsp)) { + if (required != null && !Hudson.adminCheck(req, rsp)) { // check failed. commit the FORBIDDEN response, then abort. rsp.setStatus(HttpServletResponse.SC_FORBIDDEN); rsp.getOutputStream().close(); @@ -616,8 +647,9 @@ public class Functions { } // make sure the user owns the necessary permission to access this page. - if(permission!=null) + if (permission != null) { checkPermission(permission); + } } /** @@ -625,14 +657,16 @@ public class Functions { */ public static String inferHudsonURL(StaplerRequest req) { String rootUrl = Hudson.getInstance().getRootUrl(); - if(rootUrl !=null) - // prefer the one explicitly configured, to work with load-balancer, frontend, etc. + if (rootUrl != null) // prefer the one explicitly configured, to work with load-balancer, frontend, etc. + { return rootUrl; + } StringBuilder buf = new StringBuilder(); buf.append(req.getScheme()).append("://"); buf.append(req.getServerName()); - if(req.getLocalPort()!=80) + if (req.getLocalPort() != 80) { buf.append(':').append(req.getLocalPort()); + } buf.append(req.getContextPath()).append('/'); return buf.toString(); } @@ -641,7 +675,7 @@ public class Functions { return JobPropertyDescriptor.getPropertyDescriptors(clazz); } - public static List<Descriptor<BuildWrapper>> getBuildWrapperDescriptors(AbstractProject<?,?> project) { + public static List<Descriptor<BuildWrapper>> getBuildWrapperDescriptors(AbstractProject<?, ?> project) { return BuildWrappers.getFor(project); } @@ -653,20 +687,20 @@ public class Functions { return AuthorizationStrategy.all(); } - public static List<Descriptor<Builder>> getBuilderDescriptors(AbstractProject<?,?> project) { + public static List<Descriptor<Builder>> getBuilderDescriptors(AbstractProject<?, ?> project) { return BuildStepDescriptor.filter(Builder.all(), project.getClass()); } - public static List<Descriptor<Publisher>> getPublisherDescriptors(AbstractProject<?,?> project) { + public static List<Descriptor<Publisher>> getPublisherDescriptors(AbstractProject<?, ?> project) { return BuildStepDescriptor.filter(Publisher.all(), project.getClass()); } - public static List<SCMDescriptor<?>> getSCMDescriptors(AbstractProject<?,?> project) { + public static List<SCMDescriptor<?>> getSCMDescriptors(AbstractProject<?, ?> project) { return SCM._for(project); } public static List<Descriptor<ComputerLauncher>> getComputerLauncherDescriptors() { - return Hudson.getInstance().<ComputerLauncher,Descriptor<ComputerLauncher>>getDescriptorList(ComputerLauncher.class); + return Hudson.getInstance().<ComputerLauncher, Descriptor<ComputerLauncher>>getDescriptorList(ComputerLauncher.class); } public static List<Descriptor<RetentionStrategy<?>>> getRetentionStrategyDescriptors() { @@ -680,7 +714,7 @@ public class Functions { public static List<Descriptor<ViewsTabBar>> getViewsTabBarDescriptors() { return ViewsTabBar.all(); } - + public static List<Descriptor<CaptchaSupport>> getCaptchaSupportDescriptors() { return CaptchaSupport.all(); } @@ -688,7 +722,7 @@ public class Functions { public static List<Descriptor<MyViewsTabBar>> getMyViewsTabBarDescriptors() { return MyViewsTabBar.all(); } - + public static List<Descriptor<ScriptSupport>> getScriptSupportDescriptors() { return ScriptSupport.all(); } @@ -703,7 +737,6 @@ public class Functions { } return result; } - private static final Set<String> globalConfigIgnoredDescriptors = new HashSet<String>(); /** @@ -716,99 +749,109 @@ public class Functions { } /** - * Gets all the descriptors sorted by their inheritance tree of {@link Describable} - * so that descriptors of similar types come nearby. + * Gets all the descriptors sorted by their inheritance tree of + * {@link Describable} so that descriptors of similar types come nearby. */ public static Collection<Descriptor> getSortedDescriptorsForGlobalConfig() { - Map<String,Descriptor> r = new TreeMap<String, Descriptor>(); + Map<String, Descriptor> r = new TreeMap<String, Descriptor>(); for (Descriptor<?> d : Hudson.getInstance().getExtensionList(Descriptor.class)) { if (globalConfigIgnoredDescriptors.contains(d.getClass().getName())) { continue; } - if (d.getGlobalConfigPage()==null) continue; - r.put(buildSuperclassHierarchy(d.clazz, new StringBuilder()).toString(),d); + if (d.getGlobalConfigPage() == null) { + continue; + } + r.put(buildSuperclassHierarchy(d.clazz, new StringBuilder()).toString(), d); } return r.values(); } private static StringBuilder buildSuperclassHierarchy(Class c, StringBuilder buf) { Class sc = c.getSuperclass(); - if (sc!=null) buildSuperclassHierarchy(sc,buf).append(':'); + if (sc != null) { + buildSuperclassHierarchy(sc, buf).append(':'); + } return buf.append(c.getName()); } /** - * Computes the path to the icon of the given action - * from the context path. + * Computes the path to the icon of the given action from the context path. */ public static String getIconFilePath(Action a) { String name = a.getIconFileName(); - if(name.startsWith("/")) + if (name.startsWith("/")) { return name.substring(1); - else - return "images/24x24/"+name; + } else { + return "images/24x24/" + name; + } } /** - * Works like JSTL build-in size(x) function, - * but handle null gracefully. + * Works like JSTL build-in size(x) function, but handle null gracefully. */ public static int size2(Object o) throws Exception { - if(o==null) return 0; - return ASTSizeFunction.sizeOf(o,Introspector.getUberspect()); + if (o == null) { + return 0; + } + return ASTSizeFunction.sizeOf(o, Introspector.getUberspect()); } /** * Computes the relative path from the current page to the given item. */ public static String getRelativeLinkTo(Item p) { - Map<Object,String> ancestors = new HashMap<Object,String>(); - View view=null; + Map<Object, String> ancestors = new HashMap<Object, String>(); + View view = null; StaplerRequest request = Stapler.getCurrentRequest(); - for( Ancestor a : request.getAncestors() ) { - ancestors.put(a.getObject(),a.getRelativePath()); - if(a.getObject() instanceof View) + for (Ancestor a : request.getAncestors()) { + ancestors.put(a.getObject(), a.getRelativePath()); + if (a.getObject() instanceof View) { view = (View) a.getObject(); + } } String path = ancestors.get(p); - if(path!=null) return path; + if (path != null) { + return path; + } - Item i=p; + Item i = p; String url = ""; - while(true) { + while (true) { ItemGroup ig = i.getParent(); - url = i.getShortUrl()+url; + url = i.getShortUrl() + url; - if(ig==Hudson.getInstance()) { + if (ig == Hudson.getInstance()) { assert i instanceof TopLevelItem; - if(view!=null && view.contains((TopLevelItem)i)) { + if (view != null && view.contains((TopLevelItem) i)) { // if p and the current page belongs to the same view, then return a relative path - return ancestors.get(view)+'/'+url; + return ancestors.get(view) + '/' + url; } else { // otherwise return a path from the root Hudson - return request.getContextPath()+'/'+p.getUrl(); + return request.getContextPath() + '/' + p.getUrl(); } } path = ancestors.get(ig); - if(path!=null) return path+'/'+url; + if (path != null) { + return path + '/' + url; + } assert ig instanceof Item; // if not, ig must have been the Hudson instance i = (Item) ig; } } - public static Map<Thread,StackTraceElement[]> dumpAllThreads() { - Map<Thread,StackTraceElement[]> sorted = new TreeMap<Thread,StackTraceElement[]>(new ThreadSorter()); + public static Map<Thread, StackTraceElement[]> dumpAllThreads() { + Map<Thread, StackTraceElement[]> sorted = new TreeMap<Thread, StackTraceElement[]>(new ThreadSorter()); sorted.putAll(Thread.getAllStackTraces()); return sorted; } public static ThreadInfo[] getThreadInfos() { ThreadMXBean mbean = ManagementFactory.getThreadMXBean(); - return mbean.dumpAllThreads(mbean.isObjectMonitorUsageSupported(),mbean.isSynchronizerUsageSupported()); + return mbean.dumpAllThreads(mbean.isObjectMonitorUsageSupported(), mbean.isSynchronizerUsageSupported()); } public static ThreadGroupMap sortThreadsAndGetGroupMap(ThreadInfo[] list) { @@ -819,22 +862,27 @@ public class Functions { // Common code for sorting Threads/ThreadInfos by ThreadGroup private static class ThreadSorterBase { - protected Map<Long,String> map = new HashMap<Long,String>(); + + protected Map<Long, String> map = new HashMap<Long, String>(); private ThreadSorterBase() { ThreadGroup tg = Thread.currentThread().getThreadGroup(); - while (tg.getParent() != null) tg = tg.getParent(); - Thread[] threads = new Thread[tg.activeCount()*2]; + while (tg.getParent() != null) { + tg = tg.getParent(); + } + Thread[] threads = new Thread[tg.activeCount() * 2]; int threadsLen = tg.enumerate(threads, true); - for (int i = 0; i < threadsLen; i++) + for (int i = 0; i < threadsLen; i++) { map.put(threads[i].getId(), threads[i].getThreadGroup().getName()); + } } protected int compare(long idA, long idB) { String tga = map.get(idA), tgb = map.get(idB); - int result = (tga!=null?-1:0) + (tgb!=null?1:0); // Will be non-zero if only one is null - if (result==0 && tga!=null) + int result = (tga != null ? -1 : 0) + (tgb != null ? 1 : 0); // Will be non-zero if only one is null + if (result == 0 && tga != null) { result = tga.compareToIgnoreCase(tgb); + } return result; } } @@ -848,20 +896,24 @@ public class Functions { return map.get(ti.getThreadId()); } + @Override public int compare(ThreadInfo a, ThreadInfo b) { int result = compare(a.getThreadId(), b.getThreadId()); - if (result == 0) + if (result == 0) { result = a.getThreadName().compareToIgnoreCase(b.getThreadName()); + } return result; } } private static class ThreadSorter extends ThreadSorterBase implements Comparator<Thread> { + @Override public int compare(Thread a, Thread b) { int result = compare(a.getId(), b.getId()); - if (result == 0) + if (result == 0) { result = a.getName().compareToIgnoreCase(b.getName()); + } return result; } } @@ -873,7 +925,7 @@ public class Functions { try { System.console(); return true; - } catch(LinkageError e) { + } catch (LinkageError e) { return false; } } @@ -881,16 +933,16 @@ public class Functions { // ThreadInfo.toString() truncates the stack trace by first 8, so needed my own version public static String dumpThreadInfo(ThreadInfo ti, ThreadGroupMap map) { String grp = map.getThreadGroup(ti); - StringBuilder sb = new StringBuilder("\"" + ti.getThreadName() + "\"" + - " Id=" + ti.getThreadId() + " Group=" + - (grp != null ? grp : "?") + " " + - ti.getThreadState()); + StringBuilder sb = new StringBuilder("\"" + ti.getThreadName() + "\"" + + " Id=" + ti.getThreadId() + " Group=" + + (grp != null ? grp : "?") + " " + + ti.getThreadState()); if (ti.getLockName() != null) { sb.append(" on " + ti.getLockName()); } if (ti.getLockOwnerName() != null) { - sb.append(" owned by \"" + ti.getLockOwnerName() + - "\" Id=" + ti.getLockOwnerId()); + sb.append(" owned by \"" + ti.getLockOwnerName() + + "\" Id=" + ti.getLockOwnerId()); } if (ti.isSuspended()) { sb.append(" (suspended)"); @@ -900,7 +952,7 @@ public class Functions { } sb.append('\n'); StackTraceElement[] stackTrace = ti.getStackTrace(); - for (int i=0; i < stackTrace.length; i++) { + for (int i = 0; i < stackTrace.length; i++) { StackTraceElement ste = stackTrace[i]; sb.append("\tat " + ste.toString()); sb.append('\n'); @@ -929,19 +981,19 @@ public class Functions { sb.append('\n'); } } - } + } - LockInfo[] locks = ti.getLockedSynchronizers(); - if (locks.length > 0) { - sb.append("\n\tNumber of locked synchronizers = " + locks.length); - sb.append('\n'); - for (LockInfo li : locks) { - sb.append("\t- " + li); - sb.append('\n'); - } - } - sb.append('\n'); - return sb.toString(); + LockInfo[] locks = ti.getLockedSynchronizers(); + if (locks.length > 0) { + sb.append("\n\tNumber of locked synchronizers = " + locks.length); + sb.append('\n'); + for (LockInfo li : locks) { + sb.append("\t- " + li); + sb.append('\n'); + } + } + sb.append('\n'); + return sb.toString(); } public static <T> Collection<T> emptyList() { @@ -950,20 +1002,20 @@ public class Functions { public static String jsStringEscape(String s) { StringBuilder buf = new StringBuilder(); - for( int i=0; i<s.length(); i++ ) { + for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); - switch(ch) { - case '\'': - buf.append("\\'"); - break; - case '\\': - buf.append("\\\\"); - break; - case '"': - buf.append("\\\""); - break; - default: - buf.append(ch); + switch (ch) { + case '\'': + buf.append("\\'"); + break; + case '\\': + buf.append("\\\\"); + break; + case '"': + buf.append("\\\""); + break; + default: + buf.append(ch); } } return buf.toString(); @@ -973,8 +1025,10 @@ public class Functions { * Converts "abc" to "Abc". */ public static String capitalize(String s) { - if(s==null || s.length()==0) return s; - return Character.toUpperCase(s.charAt(0))+s.substring(1); + if (s == null || s.length() == 0) { + return s; + } + return Character.toUpperCase(s.charAt(0)) + s.substring(1); } public static String getVersion() { @@ -991,43 +1045,49 @@ public class Functions { public static String getViewResource(Object it, String path) { Class clazz = it.getClass(); - if(it instanceof Class) - clazz = (Class)it; - if(it instanceof Descriptor) - clazz = ((Descriptor)it).clazz; + if (it instanceof Class) { + clazz = (Class) it; + } + if (it instanceof Descriptor) { + clazz = ((Descriptor) it).clazz; + } StringBuilder buf = new StringBuilder(Stapler.getCurrentRequest().getContextPath()); buf.append(Hudson.VIEW_RESOURCE_PATH).append('/'); - buf.append(clazz.getName().replace('.','/').replace('$','/')); + buf.append(clazz.getName().replace('.', '/').replace('$', '/')); buf.append('/').append(path); return buf.toString(); } public static boolean hasView(Object it, String path) throws IOException { - if(it==null) return false; - return Stapler.getCurrentRequest().getView(it,path)!=null; + if (it == null) { + return false; + } + return Stapler.getCurrentRequest().getView(it, path) != null; } /** - * Can be used to check a checkbox by default. - * Used from views like {@code h.defaultToTrue(scm.useUpdate)}. - * The expression will evaluate to true if scm is null. + * Can be used to check a checkbox by default. Used from views like + * {@code h.defaultToTrue(scm.useUpdate)}. The expression will evaluate to + * true if scm is null. */ public static boolean defaultToTrue(Boolean b) { - if(b==null) return true; + if (b == null) { + return true; + } return b; } /** - * If the value exists, return that value. Otherwise return the default value. - * <p> - * Starting 1.294, JEXL supports the elvis operator "x?:y" that supercedes this. + * If the value exists, return that value. Otherwise return the default + * value. <p> Starting 1.294, JEXL supports the elvis operator "x?:y" that + * supercedes this. * * @since 1.150 */ public static <T> T defaulted(T value, T defaultValue) { - return value!=null ? value : defaultValue; + return value != null ? value : defaultValue; } public static String printThrowable(Throwable t) { @@ -1037,36 +1097,37 @@ public class Functions { } /** - * Counts the number of rows needed for textarea to fit the content. - * Minimum 5 rows. + * Counts the number of rows needed for textarea to fit the content. Minimum + * 5 rows. */ public static int determineRows(String s) { - if(s==null) return 5; - return Math.max(5,LINE_END.split(s).length); + if (s == null) { + return 5; + } + return Math.max(5, LINE_END.split(s).length); } /** - * Converts the Hudson build status to CruiseControl build status, - * which is either Success, Failure, Exception, or Unknown. + * Converts the Hudson build status to CruiseControl build status, which is + * either Success, Failure, Exception, or Unknown. */ public static String toCCStatus(Item i) { if (i instanceof Job) { Job j = (Job) i; switch (j.getIconColor().noAnime()) { - case ABORTED: - case RED: - case YELLOW: - return "Failure"; - case BLUE: - return "Success"; - case DISABLED: - case GREY: - return "Unknown"; + case ABORTED: + case RED: + case YELLOW: + return "Failure"; + case BLUE: + return "Success"; + case DISABLED: + case GREY: + return "Unknown"; } } return "Unknown"; } - private static final Pattern LINE_END = Pattern.compile("\r?\n"); /** @@ -1077,15 +1138,14 @@ public class Functions { } /** - * When called from within JEXL expression evaluation, - * this method returns the current {@link JellyContext} used - * to evaluate the script. + * When called from within JEXL expression evaluation, this method returns + * the current {@link JellyContext} used to evaluate the script. * * @since 1.164 */ public static JellyContext getCurrentJellyContext() { JellyContext context = org.eclipse.hudson.ExpressionFactory2.CURRENT_CONTEXT.get(); - assert context!=null; + assert context != null; return context; } @@ -1101,49 +1161,55 @@ public class Functions { } /** - * Returns a sub-list if the given list is bigger than the specified 'maxSize' + * Returns a sub-list if the given list is bigger than the specified + * 'maxSize' */ public static <T> List<T> subList(List<T> base, int maxSize) { - if(maxSize<base.size()) - return base.subList(0,maxSize); - else + if (maxSize < base.size()) { + return base.subList(0, maxSize); + } else { return base; + } } /** - * Computes the hyperlink to actions, to handle the situation when the {@link Action#getUrlName()} - * returns absolute URL. + * Computes the hyperlink to actions, to handle the situation when the + * {@link Action#getUrlName()} returns absolute URL. */ - public static String getActionUrl(String itUrl,Action action) { + public static String getActionUrl(String itUrl, Action action) { String urlName = action.getUrlName(); - if(urlName==null) return null; // to avoid NPE and fail to render the whole page - - if(SCHEME.matcher(urlName).matches()) + if (urlName == null) { + return null; // to avoid NPE and fail to render the whole page + } + if (SCHEME.matcher(urlName).matches()) { return urlName; // absolute URL - if(urlName.startsWith("/")) - return Stapler.getCurrentRequest().getContextPath()+urlName; - else - // relative URL name - return Stapler.getCurrentRequest().getContextPath()+'/'+itUrl+urlName; + } + if (urlName.startsWith("/")) { + return Stapler.getCurrentRequest().getContextPath() + urlName; + } else // relative URL name + { + return Stapler.getCurrentRequest().getContextPath() + '/' + itUrl + urlName; + } } /** - * Escapes the character unsafe for e-mail address. - * See http://en.wikipedia.org/wiki/E-mail_address for the details, - * but here the vocabulary is even more restricted. + * Escapes the character unsafe for e-mail address. See + * http://en.wikipedia.org/wiki/E-mail_address for the details, but here the + * vocabulary is even more restricted. */ public static String toEmailSafeString(String projectName) { // TODO: escape non-ASCII characters StringBuilder buf = new StringBuilder(projectName.length()); - for( int i=0; i<projectName.length(); i++ ) { + for (int i = 0; i < projectName.length(); i++) { char ch = projectName.charAt(i); - if(('a'<=ch && ch<='z') - || ('z'<=ch && ch<='Z') - || ('0'<=ch && ch<='9') - || "-_.".indexOf(ch)>=0) + if (('a' <= ch && ch <= 'z') + || ('z' <= ch && ch <= 'Z') + || ('0' <= ch && ch <= '9') + || "-_.".indexOf(ch) >= 0) { buf.append(ch); - else + } else { buf.append('_'); // escape + } } return projectName; } @@ -1153,20 +1219,20 @@ public class Functions { } /** - * Obtains the host name of the Hudson server that clients can use to talk back to. - * <p> - * This is primarily used in <tt>slave-agent.jnlp.jelly</tt> to specify the destination - * that the slaves talk to. + * Obtains the host name of the Hudson server that clients can use to talk + * back to. <p> This is primarily used in <tt>slave-agent.jnlp.jelly</tt> to + * specify the destination that the slaves talk to. */ public String getServerName() { // Try to infer this from the configured root URL. // This makes it work correctly when Hudson runs behind a reverse proxy. String url = Hudson.getInstance().getRootUrl(); try { - if(url!=null) { + if (url != null) { String host = new URL(url).getHost(); - if(host!=null) + if (host != null) { return host; + } } } catch (MalformedURLException e) { // fall back to HTTP request @@ -1178,7 +1244,9 @@ public class Functions { * Determines the form validation check URL. See textbox.jelly */ public String getCheckUrl(String userDefined, Object descriptor, String field) { - if(userDefined!=null || field==null) return userDefined; + if (userDefined != null || field == null) { + return userDefined; + } if (descriptor instanceof Descriptor) { Descriptor d = (Descriptor) descriptor; return d.getCheckUrl(field); @@ -1193,11 +1261,17 @@ public class Functions { */ public boolean hyperlinkMatchesCurrentPage(String href) throws UnsupportedEncodingException { String url = Stapler.getCurrentRequest().getRequestURL().toString(); - if (href == null || href.length() <= 1) return ".".equals(href) && url.endsWith("/"); - url = URLDecoder.decode(url,"UTF-8"); - href = URLDecoder.decode(href,"UTF-8"); - if (url.endsWith("/")) url = url.substring(0, url.length() - 1); - if (href.endsWith("/")) href = href.substring(0, href.length() - 1); + if (href == null || href.length() <= 1) { + return ".".equals(href) && url.endsWith("/"); + } + url = URLDecoder.decode(url, "UTF-8"); + href = URLDecoder.decode(href, "UTF-8"); + if (url.endsWith("/")) { + url = url.substring(0, url.length() - 1); + } + if (href.endsWith("/")) { + href = href.substring(0, href.length() - 1); + } return url.endsWith(href); } @@ -1211,10 +1285,12 @@ public class Functions { */ public static List<PageDecorator> getPageDecorators() { // this method may be called to render start up errors, at which point Hudson doesn't exist yet. see HUDSON-3608 - if(Hudson.getInstance()==null) return Collections.emptyList(); + if (Hudson.getInstance() == null) { + return Collections.emptyList(); + } return PageDecorator.all(); } - + public static List<Descriptor<Cloud>> getCloudDescriptors() { return Cloud.all(); } @@ -1223,8 +1299,9 @@ public class Functions { * Prepend a prefix only when there's the specified body. */ public String prepend(String prefix, String body) { - if(body!=null && body.length()>0) - return prefix+body; + if (body != null && body.length() > 0) { + return prefix + body; + } return body; } @@ -1247,35 +1324,40 @@ public class Functions { public static Date getCurrentTime() { return new Date(); } - - public static Locale getClientLocale(){ + + public static Locale getClientLocale() { return Stapler.getCurrentRequest().getLocale(); } - - public static Locale getServerLocale(){ + + public static Locale getServerLocale() { return Locale.getDefault(); } /** - * Generate a series of <script> tags to include <tt>script.js</tt> - * from {@link ConsoleAnnotatorFactory}s and {@link ConsoleAnnotationDescriptor}s. + * Generate a series of <script> tags to include <tt>script.js</tt> from + * {@link ConsoleAnnotatorFactory}s and + * {@link ConsoleAnnotationDescriptor}s. */ public static String generateConsoleAnnotationScriptAndStylesheet() { String cp = Stapler.getCurrentRequest().getContextPath(); StringBuilder buf = new StringBuilder(); for (ConsoleAnnotatorFactory f : ConsoleAnnotatorFactory.all()) { String path = cp + "/extensionList/" + ConsoleAnnotatorFactory.class.getName() + "/" + f.getClass().getName(); - if (f.hasScript()) - buf.append("<script src='"+path+"/script.js'></script>"); - if (f.hasStylesheet()) - buf.append("<link rel='stylesheet' type='text/css' href='"+path+"/style.css' />"); + if (f.hasScript()) { + buf.append("<script src='" + path + "/script.js'></script>"); + } + if (f.hasStylesheet()) { + buf.append("<link rel='stylesheet' type='text/css' href='" + path + "/style.css' />"); + } } for (ConsoleAnnotationDescriptor d : ConsoleAnnotationDescriptor.all()) { - String path = cp+"/descriptor/"+d.clazz.getName(); - if (d.hasScript()) - buf.append("<script src='"+path+"/script.js'></script>"); - if (d.hasStylesheet()) - buf.append("<link rel='stylesheet' type='text/css' href='"+path+"/style.css' />"); + String path = cp + "/descriptor/" + d.clazz.getName(); + if (d.hasScript()) { + buf.append("<script src='" + path + "/script.js'></script>"); + } + if (d.hasStylesheet()) { + buf.append("<link rel='stylesheet' type='text/css' href='" + path + "/style.css' />"); + } } return buf.toString(); } @@ -1288,8 +1370,9 @@ public class Functions { try { List<String> r = new ArrayList<String>(); Enumeration<String> e = LogManager.getLogManager().getLoggerNames(); - while (e.hasMoreElements()) + while (e.hasMoreElements()) { r.add(e.nextElement()); + } return r; } catch (ConcurrentModificationException e) { // retry @@ -1298,18 +1381,22 @@ public class Functions { } /** - * Used by <f:password/> so that we send an encrypted value to the client. + * Used by <f:password/> so that we send an encrypted value to the + * client. */ public String getPasswordValue(Object o) { - if (o==null) return null; - if (o instanceof Secret) return ((Secret)o).getEncryptedValue(); + if (o == null) { + return null; + } + if (o instanceof Secret) { + return ((Secret) o).getEncryptedValue(); + } return o.toString(); } public List filterDescriptors(Object context, Iterable descriptors) { - return DescriptorVisibilityFilter.apply(context,descriptors); + return DescriptorVisibilityFilter.apply(context, descriptors); } - private static final Pattern SCHEME = Pattern.compile("[a-z]+://.+"); /** @@ -1336,6 +1423,7 @@ public class Functions { /** * Returns true if current user is the author of the job. + * * @param job job. * @return returns true if current user is the author of the job. */ @@ -1345,13 +1433,14 @@ public class Functions { } /** - * Resolves the target object for the given object. If the object is a StaplerProxy, then return the proxy target. + * Resolves the target object for the given object. If the object is a + * StaplerProxy, then return the proxy target. * * @since 2.1.0 */ public static Object resolveStaplerObject(final Object obj) { if (obj instanceof StaplerProxy) { - return ((StaplerProxy)obj).getTarget(); + return ((StaplerProxy) obj).getTarget(); } return obj; } @@ -1368,6 +1457,7 @@ public class Functions { return null; } Iterable<T> templates = Iterables.filter(items, new Predicate<T>() { + @Override public boolean apply(T item) { return name.equalsIgnoreCase(item.getName()); } @@ -1378,17 +1468,19 @@ public class Functions { /** * Returns true if the {@link Item#WIPEOUT} permission is enabled. * - * By default the "Wipe Out Workspace" action is available on job when user has {@link Item#BUILD} permission - * (if user can trigger builds). If this behavior is not acceptable for project you can enable the - * {@code hudson.security.WipeOutPermission} system property. It will add "WipeOut" permission checkbox into - * permission control panel to manage "Wipe Out Workspace" action. + * By default the "Wipe Out Workspace" action is available on job when user + * has {@link Item#BUILD} permission (if user can trigger builds). If this + * behavior is not acceptable for project you can enable the + * {@code hudson.security.WipeOutPermission} system property. It will add + * "WipeOut" permission checkbox into permission control panel to manage + * "Wipe Out Workspace" action. * * @return true if the {@link Item#WIPEOUT} permission is enabled. */ public static boolean isWipeOutPermissionEnabled() { return Boolean.getBoolean("hudson.security.WipeOutPermission"); } - + public static boolean disableUpdateCenterSwitch() { return Boolean.getBoolean("hudson.pluginManager.disableUpdateCenterSwitch"); } diff --git a/hudson-core/src/main/java/hudson/PluginManager.java b/hudson-core/src/main/java/hudson/PluginManager.java index 621c59f..1af0e60 100644 --- a/hudson-core/src/main/java/hudson/PluginManager.java +++ b/hudson-core/src/main/java/hudson/PluginManager.java @@ -7,10 +7,10 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * - * Contributors: + * Contributors: * * Kohsuke Kawaguchi, Stephen Connolly, Tom Huybrechts, Winston Prakash - * + * *******************************************************************************/ package hudson; @@ -75,57 +75,50 @@ import static hudson.init.InitMilestone.PLUGINS_STARTED; * @author Kohsuke Kawaguchi */ public abstract class PluginManager extends AbstractModelObject { + /** * All discovered plugins. */ protected final List<PluginWrapper> plugins = new ArrayList<PluginWrapper>(); - /** * All active plugins. */ - protected final List<PluginWrapper> activePlugins = new CopyOnWriteArrayList<PluginWrapper> (); - + protected final List<PluginWrapper> activePlugins = new CopyOnWriteArrayList<PluginWrapper>(); protected final List<FailedPlugin> failedPlugins = new ArrayList<FailedPlugin>(); - /** * Plug-in root directory. */ //TODO: review and check whether we can do it private public final File rootDir; - /** - * @deprecated as of 1.355 - * {@link PluginManager} can now live longer than {@link Hudson} instance, so - * use {@code Hudson.getInstance().servletContext} instead. + * @deprecated as of 1.355 {@link PluginManager} can now live longer than + * {@link Hudson} instance, so use + * {@code Hudson.getInstance().servletContext} instead. */ public final ServletContext context; - /** - * {@link ClassLoader} that can load all the publicly visible classes from plugins - * (and including the classloader that loads Hudson itself.) + * {@link ClassLoader} that can load all the publicly visible classes from + * plugins (and including the classloader that loads Hudson itself.) * */ // implementation is minimal --- just enough to run XStream // and load plugin-contributed classes. //TODO: review and check whether we can do it private public final ClassLoader uberClassLoader = new UberClassLoader(); - /** - * Once plugin is uploaded, this flag becomes true. - * This is used to report a message that Hudson needs to be restarted - * for new plugins to take effect. + * Once plugin is uploaded, this flag becomes true. This is used to report a + * message that Hudson needs to be restarted for new plugins to take effect. */ //TODO: review and check whether we can do it private public volatile boolean pluginUploaded = false; - /** - * The initialization of {@link PluginManager} splits into two parts; - * one is the part about listing them, extracting them, and preparing classloader for them. - * The 2nd part is about creating instances. Once the former completes this flags become true, - * as the 2nd part can be repeated for each Hudson instance. + * The initialization of {@link PluginManager} splits into two parts; one is + * the part about listing them, extracting them, and preparing classloader + * for them. The 2nd part is about creating instances. Once the former + * completes this flags become true, as the 2nd part can be repeated for + * each Hudson instance. */ private boolean pluginListed = false; - /** * Strategy for creating and initializing plugins */ @@ -135,9 +128,10 @@ public abstract class PluginManager extends AbstractModelObject { this.context = context; this.rootDir = rootDir; - if(!rootDir.exists()) + if (!rootDir.exists()) { rootDir.mkdirs(); - + } + strategy = createPluginStrategy(); } @@ -158,9 +152,9 @@ public abstract class PluginManager extends AbstractModelObject { } /** - * Called immediately after the construction. - * This is a separate method so that code executed from here will see a valid value in - * {@link Hudson#pluginManager}. + * Called immediately after the construction. This is a separate method so + * that code executed from here will see a valid value in + * {@link Hudson#pluginManager}. */ public TaskBuilder initTasks(final InitStrategy initStrategy) { TaskBuilder builder; @@ -171,78 +165,90 @@ public abstract class PluginManager extends AbstractModelObject { { Handle loadBundledPlugins = add("Loading bundled plugins", new Executable() { + @Override public void run(Reactor session) throws Exception { bundledPlugins = loadBundledPlugins(); } }); Handle listUpPlugins = requires(loadBundledPlugins).add("Listing up plugins", new Executable() { + @Override public void run(Reactor session) throws Exception { archives = initStrategy.listPluginArchives(PluginManager.this); } }); - requires(listUpPlugins).attains(PLUGINS_LISTED).add("Preparing plugins",new Executable() { + requires(listUpPlugins).attains(PLUGINS_LISTED).add("Preparing plugins", new Executable() { + @Override public void run(Reactor session) throws Exception { // once we've listed plugins, we can fill in the reactor with plugin-specific initialization tasks TaskGraphBuilder g = new TaskGraphBuilder(); - final Map<String,File> inspectedShortNames = new HashMap<String,File>(); + final Map<String, File> inspectedShortNames = new HashMap<String, File>(); - for( final File arc : archives ) { + for (final File arc : archives) { g.followedBy().notFatal().attains(PLUGINS_LISTED).add("Inspecting plugin " + arc, new Executable() { + @Override public void run(Reactor session1) throws Exception { try { PluginWrapper p = strategy.createPluginWrapper(arc); - if (isDuplicate(p)) return; + if (isDuplicate(p)) { + return; + } p.isBundled = bundledPlugins.contains(arc.getName()); plugins.add(p); - if(p.isActive()) + if (p.isActive()) { activePlugins.add(p); + } } catch (IOException e) { - failedPlugins.add(new FailedPlugin(arc.getName(),e)); + failedPlugins.add(new FailedPlugin(arc.getName(), e)); throw e; } } /** - * Inspects duplication. this happens when you run hpi:run on a bundled plugin, - * as well as putting numbered hpi files, like "cobertura-1.0.hpi" and "cobertura-1.1.hpi" + * Inspects duplication. this happens when + * you run hpi:run on a bundled plugin, as + * well as putting numbered hpi files, like + * "cobertura-1.0.hpi" and + * "cobertura-1.1.hpi" */ private boolean isDuplicate(PluginWrapper p) { String shortName = p.getShortName(); if (inspectedShortNames.containsKey(shortName)) { - LOGGER.info("Ignoring "+arc+" because "+inspectedShortNames.get(shortName)+" is already loaded"); + LOGGER.info("Ignoring " + arc + " because " + inspectedShortNames.get(shortName) + " is already loaded"); return true; } - inspectedShortNames.put(shortName,arc); + inspectedShortNames.put(shortName, arc); return false; } }); } - g.requires(PLUGINS_PREPARED).add("Checking cyclic dependencies",new Executable() { + g.requires(PLUGINS_PREPARED).add("Checking cyclic dependencies", new Executable() { /** * Makes sure there's no cycle in dependencies. */ + @Override public void run(Reactor reactor) throws Exception { try { new CyclicGraphDetector<PluginWrapper>() { @Override protected List<PluginWrapper> getEdges(PluginWrapper p) { List<PluginWrapper> next = new ArrayList<PluginWrapper>(); - addTo(p.getDependencies(),next); - addTo(p.getOptionalDependencies(),next); + addTo(p.getDependencies(), next); + addTo(p.getOptionalDependencies(), next); return next; } private void addTo(List<Dependency> dependencies, List<PluginWrapper> r) { for (Dependency d : dependencies) { PluginWrapper p = getPlugin(d.shortName); - if (p!=null) + if (p != null) { r.add(p); + } } } }.run(getPlugins()); @@ -269,82 +275,89 @@ public abstract class PluginManager extends AbstractModelObject { // lists up initialization tasks about loading plugins. return TaskBuilder.union(initializerFinder, // this scans @Initializer in the core once - builder,new TaskGraphBuilder() {{ - requires(PLUGINS_LISTED).attains(PLUGINS_PREPARED).add("Loading plugins",new Executable() { - /** - * Once the plugins are listed, schedule their initialization. - */ - public void run(Reactor session) throws Exception { - Hudson.getInstance().lookup.set(PluginInstanceStore.class,new PluginInstanceStore()); - TaskGraphBuilder g = new TaskGraphBuilder(); - - // schedule execution of loading plugins - for (final PluginWrapper p : activePlugins.toArray(new PluginWrapper[activePlugins.size()])) { - g.followedBy().notFatal().attains(PLUGINS_PREPARED).add("Loading plugin " + p.getShortName(), new Executable() { - public void run(Reactor session) throws Exception { - try { - LOGGER.info("Loading plugin - " + p.getShortName()); - p.resolvePluginDependencies(); - strategy.load(p); - } catch (Exception e) { - LOGGER.info("Failed to load plugin - " + p.getShortName() + " because of error " + e.getLocalizedMessage()); - failedPlugins.add(new FailedPlugin(p.getShortName(), e)); - activePlugins.remove(p); - plugins.remove(p); - throw e; - } - } - }); - } + builder, new TaskGraphBuilder() { + { + requires(PLUGINS_LISTED).attains(PLUGINS_PREPARED).add("Loading plugins", new Executable() { + /** + * Once the plugins are listed, schedule their + * initialization. + */ + public void run(Reactor session) throws Exception { + Hudson.getInstance().lookup.set(PluginInstanceStore.class, new PluginInstanceStore()); + TaskGraphBuilder g = new TaskGraphBuilder(); + + // schedule execution of loading plugins + for (final PluginWrapper p : activePlugins.toArray(new PluginWrapper[activePlugins.size()])) { + g.followedBy().notFatal().attains(PLUGINS_PREPARED).add("Loading plugin " + p.getShortName(), new Executable() { + @Override + public void run(Reactor session) throws Exception { + try { + LOGGER.info("Loading plugin - " + p.getShortName()); + p.resolvePluginDependencies(); + strategy.load(p); + } catch (Exception e) { + LOGGER.info("Failed to load plugin - " + p.getShortName() + " because of error " + e.getLocalizedMessage()); + failedPlugins.add(new FailedPlugin(p.getShortName(), e)); + activePlugins.remove(p); + plugins.remove(p); + throw e; + } + } + }); + } - // schedule execution of initializing plugins - for (final PluginWrapper p : activePlugins.toArray(new PluginWrapper[activePlugins.size()])) { - g.followedBy().notFatal().attains(PLUGINS_STARTED).add("Initializing plugin " + p.getShortName(), new Executable() { - public void run(Reactor session) throws Exception { - try { - p.getPlugin().postInitialize(); - } catch (Exception e) { - failedPlugins.add(new FailedPlugin(p.getShortName(), e)); - activePlugins.remove(p); - plugins.remove(p); - throw e; + // schedule execution of initializing plugins + for (final PluginWrapper p : activePlugins.toArray(new PluginWrapper[activePlugins.size()])) { + g.followedBy().notFatal().attains(PLUGINS_STARTED).add("Initializing plugin " + p.getShortName(), new Executable() { + @Override + public void run(Reactor session) throws Exception { + try { + p.getPlugin().postInitialize(); + } catch (Exception e) { + failedPlugins.add(new FailedPlugin(p.getShortName(), e)); + activePlugins.remove(p); + plugins.remove(p); + throw e; + } } + }); + } + + g.followedBy().attains(PLUGINS_STARTED).add("Discovering plugin initialization tasks", new Executable() { + @Override + public void run(Reactor reactor) throws Exception { + // rescan to find plugin-contributed @Initializer + reactor.addAll(initializerFinder.discoverTasks(reactor)); } }); - } - - g.followedBy().attains(PLUGINS_STARTED).add("Discovering plugin initialization tasks", new Executable() { - public void run(Reactor reactor) throws Exception { - // rescan to find plugin-contributed @Initializer - reactor.addAll(initializerFinder.discoverTasks(reactor)); - } - }); - // register them all - session.addAll(g.discoverTasks(session)); - } - }); - }}); + // register them all + session.addAll(g.discoverTasks(session)); + } + }); + } + }); } /** - * If the war file has any "/WEB-INF/plugins/*.hpi", extract them into the plugin directory. + * If the war file has any "/WEB-INF/plugins/*.hpi", extract them into the + * plugin directory. * - * @return - * File names of the bundled plugins. Like {"ssh-slaves.hpi","subvesrion.hpi"} - * @throws Exception - * Any exception will be reported and halt the startup. + * @return File names of the bundled plugins. Like + * {"ssh-slaves.hpi","subvesrion.hpi"} + * @throws Exception Any exception will be reported and halt the startup. */ protected abstract Collection<String> loadBundledPlugins() throws Exception; /** - * Copies the bundled plugin from the given URL to the destination of the given file name (like 'abc.hpi'), - * with a reasonable up-to-date check. A convenience method to be used by the {@link #loadBundledPlugins()}. + * Copies the bundled plugin from the given URL to the destination of the + * given file name (like 'abc.hpi'), with a reasonable up-to-date check. A + * convenience method to be used by the {@link #loadBundledPlugins()}. */ protected void copyBundledPlugin(URL src, String fileName) throws IOException { long lastModified = src.openConnection().getLastModified(); File file = new File(rootDir, fileName); - File pinFile = new File(rootDir, fileName+".pinned"); + File pinFile = new File(rootDir, fileName + ".pinned"); // update file if: // - no file exists today @@ -360,7 +373,8 @@ public abstract class PluginManager extends AbstractModelObject { } /** - * Creates a hudson.PluginStrategy, looking at the corresponding system property. + * Creates a hudson.PluginStrategy, looking at the corresponding system + * property. */ private PluginStrategy createPluginStrategy() { // Allow the user to specify a the plugin strategy to use with a system property @@ -407,13 +421,13 @@ public abstract class PluginManager extends AbstractModelObject { } /** - * Returns true if any new plugin was added, which means a restart is required - * for the change to take effect. + * Returns true if any new plugin was added, which means a restart is + * required for the change to take effect. */ public boolean isPluginUploaded() { return pluginUploaded; } - + public List<PluginWrapper> getPlugins() { return plugins; } @@ -424,37 +438,43 @@ public abstract class PluginManager extends AbstractModelObject { public PluginWrapper getPlugin(String shortName) { for (PluginWrapper p : plugins) { - if(p.getShortName().equals(shortName)) + if (p.getShortName().equals(shortName)) { return p; + } } return null; } /** - * Get the plugin instance that implements a specific class, use to find your plugin singleton. - * Note: beware the classloader fun. + * Get the plugin instance that implements a specific class, use to find + * your plugin singleton. Note: beware the classloader fun. + * * @param pluginClazz The class that your plugin implements. - * @return The plugin singleton or <code>null</code> if for some reason the plugin is not loaded. + * @return The plugin singleton or <code>null</code> if for some reason the + * plugin is not loaded. */ public PluginWrapper getPlugin(Class<? extends Plugin> pluginClazz) { for (PluginWrapper p : plugins) { - if(pluginClazz.isInstance(p.getPlugin())) + if (pluginClazz.isInstance(p.getPlugin())) { return p; + } } return null; } /** - * Get the plugin instances that extend a specific class, use to find similar plugins. - * Note: beware the classloader fun. + * Get the plugin instances that extend a specific class, use to find + * similar plugins. Note: beware the classloader fun. + * * @param pluginSuperclass The class that your plugin is derived from. * @return The list of plugins implementing the specified class. */ public List<PluginWrapper> getPlugins(Class<? extends Plugin> pluginSuperclass) { List<PluginWrapper> result = new ArrayList<PluginWrapper>(); for (PluginWrapper p : plugins) { - if(pluginSuperclass.isInstance(p.getPlugin())) + if (pluginSuperclass.isInstance(p.getPlugin())) { result.add(p); + } } return Collections.unmodifiableList(result); } @@ -468,10 +488,10 @@ public abstract class PluginManager extends AbstractModelObject { } /** - * Discover all the service provider implementations of the given class, - * via <tt>META-INF/services</tt>. + * Discover all the service provider implementations of the given class, via + * <tt>META-INF/services</tt>. */ - public <T> Collection<Class<? extends T>> discover( Class<T> spi ) { + public <T> Collection<Class<? extends T>> discover(Class<T> spi) { Set<Class<? extends T>> result = new HashSet<Class<? extends T>>(); for (PluginWrapper p : activePlugins) { @@ -502,14 +522,15 @@ public abstract class PluginManager extends AbstractModelObject { UpdateCenter uc = Hudson.getInstance().getUpdateCenter(); BulkChange bc = new BulkChange(uc); try { - for (String id : req.getParameterValues("sources")) + for (String id : req.getParameterValues("sources")) { uc.getSites().remove(uc.getById(id)); + } } finally { bc.commit(); } - } else - if (req.hasParameter("add")) + } else if (req.hasParameter("add")) { return new HttpRedirect("addSite"); + } return new HttpRedirect("./sites"); } @@ -520,21 +541,21 @@ public abstract class PluginManager extends AbstractModelObject { public void doInstall(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { Enumeration<String> en = req.getParameterNames(); while (en.hasMoreElements()) { - String n = en.nextElement(); - if(n.startsWith("plugin.")) { + String n = en.nextElement(); + if (n.startsWith("plugin.")) { n = n.substring(7); if (n.indexOf(".") > 0) { String[] pluginInfo = n.split("\\."); UpdateSite.Plugin p = Hudson.getInstance().getUpdateCenter().getById(pluginInfo[1]).getPlugin(pluginInfo[0]); - if(p==null) - throw new Failure("No such plugin: "+n); + if (p == null) { + throw new Failure("No such plugin: " + n); + } p.deploy(); } } } rsp.sendRedirect("../updateCenter/"); } - /** * Bare-minimum configuration mechanism to change the update center. @@ -545,15 +566,15 @@ public abstract class PluginManager extends AbstractModelObject { UpdateCenter uc = hudson.getUpdateCenter(); PersistedList<UpdateSite> sites = uc.getSites(); for (UpdateSite s : sites) { - if (s.getId().equals("default")) + if (s.getId().equals("default")) { sites.remove(s); + } } - sites.add(new UpdateSite("default",site)); - + sites.add(new UpdateSite("default", site)); + return HttpResponses.redirectToContextRoot(); } - public HttpResponse doProxyConfigure( @QueryParameter("proxy.server") String server, @QueryParameter("proxy.port") String port, @@ -563,23 +584,23 @@ public abstract class PluginManager extends AbstractModelObject { @QueryParameter("proxy.authNeeded") String authNeeded) throws IOException { Hudson hudson = Hudson.getInstance(); hudson.checkPermission(Hudson.ADMINISTER); - + server = Util.fixEmptyAndTrim(server); if ((server != null) && !"".equals(server)) { // If port is not specified assume it is port 80 (usual default for HTTP port) int portNumber = 80; - if (!"".equals(Util.fixNull(port))){ + if (!"".equals(Util.fixNull(port))) { portNumber = Integer.parseInt(Util.fixNull(port)); } - + boolean proxyAuthNeeded = "on".equals(Util.fixNull(authNeeded)); - if (!proxyAuthNeeded){ + if (!proxyAuthNeeded) { userName = ""; password = ""; } - - hudson.proxy.configure(server , portNumber, Util.fixEmptyAndTrim(noProxyFor), + + hudson.proxy.configure(server, portNumber, Util.fixEmptyAndTrim(noProxyFor), Util.fixEmptyAndTrim(userName), Util.fixEmptyAndTrim(password), "on".equals(Util.fixNull(authNeeded))); hudson.proxy.save(); } else { @@ -602,10 +623,12 @@ public abstract class PluginManager extends AbstractModelObject { // Parse the request FileItem fileItem = (FileItem) upload.parseRequest(req).get(0); String fileName = Util.getFileName(fileItem.getName()); - if("".equals(fileName)) + if ("".equals(fileName)) { return new HttpRedirect("advanced"); - if(!fileName.endsWith(".hpi")) + } + if (!fileName.endsWith(".hpi")) { throw new Failure(hudson.model.Messages.Hudson_NotAPlugin(fileName)); + } fileItem.write(new File(rootDir, fileName)); fileItem.delete(); @@ -623,9 +646,9 @@ public abstract class PluginManager extends AbstractModelObject { * {@link ClassLoader} that can see all plugins. */ public final class UberClassLoader extends ClassLoader { + /** - * Make generated types visible. - * Keyed by the generated class name. + * Make generated types visible. Keyed by the generated class name. */ private ConcurrentMap<String, WeakReference<Class>> generatedClasses = new ConcurrentHashMap<String, WeakReference<Class>>(); @@ -634,27 +657,31 @@ public abstract class PluginManager extends AbstractModelObject { } public void addNamedClass(String className, Class c) { - generatedClasses.put(className,new WeakReference<Class>(c)); + generatedClasses.put(className, new WeakReference<Class>(c)); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { WeakReference<Class> wc = generatedClasses.get(name); - if (wc!=null) { + if (wc != null) { Class c = wc.get(); - if (c!=null) return c; - else generatedClasses.remove(name,wc); + if (c != null) { + return c; + } else { + generatedClasses.remove(name, wc); + } } // first, use the context classloader so that plugins that are loading // can use its own classloader first. ClassLoader cl = Thread.currentThread().getContextClassLoader(); - if(cl!=null && cl!=this) + if (cl != null && cl != this) { try { return cl.loadClass(name); - } catch(ClassNotFoundException e) { + } catch (ClassNotFoundException e) { // not found. try next } + } for (PluginWrapper p : activePlugins) { try { @@ -671,8 +698,9 @@ public abstract class PluginManager extends AbstractModelObject { protected URL findResource(String name) { for (PluginWrapper p : activePlugins) { URL url = p.classLoader.getResource(name); - if(url!=null) + if (url != null) { return url; + } } return null; } @@ -687,19 +715,18 @@ public abstract class PluginManager extends AbstractModelObject { } @Override - public String toString() - { + public String toString() { // only for debugging purpose - return "classLoader " + getClass().getName(); + return "classLoader " + getClass().getName(); } } - private static final Logger LOGGER = Logger.getLogger(PluginManager.class.getName()); /** * Remembers why a plugin failed to deploy. */ public static final class FailedPlugin { + public final String name; public final Exception cause; @@ -717,6 +744,7 @@ public abstract class PluginManager extends AbstractModelObject { * Stores {@link Plugin} instances. */ /*package*/ static final class PluginInstanceStore { - final Map<PluginWrapper,Plugin> store = new Hashtable<PluginWrapper,Plugin>(); + + final Map<PluginWrapper, Plugin> store = new Hashtable<PluginWrapper, Plugin>(); } } diff --git a/hudson-core/src/main/java/hudson/model/Queue.java b/hudson-core/src/main/java/hudson/model/Queue.java index 31a0197..94a1218 100644 --- a/hudson-core/src/main/java/hudson/model/Queue.java +++ b/hudson-core/src/main/java/hudson/model/Queue.java @@ -7,10 +7,10 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * - * Contributors: + * Contributors: * * Kohsuke Kawaguchi, Stephen Connolly, Tom Huybrechts, InfraDNA, Inc. - * + * * *******************************************************************************/ @@ -94,13 +94,12 @@ import java.util.HashSet; /** * Build queue. * - * <p> - * This class implements the core scheduling logic. {@link Task} represents the executable - * task that are placed in the queue. While in the queue, it's wrapped into {@link Item} - * so that we can keep track of additional data used for deciding what to exeucte when. + * <p> This class implements the core scheduling logic. {@link Task} represents + * the executable task that are placed in the queue. While in the queue, it's + * wrapped into {@link Item} so that we can keep track of additional data used + * for deciding what to exeucte when. * - * <p> - * Items in queue goes through several stages, as depicted below: + * <p> Items in queue goes through several stages, as depicted below: * <pre> * (enter) --> waitingList --+--> blockedProjects * | ^ @@ -109,38 +108,34 @@ import java.util.HashSet; * +--> buildables ---> pending ---> (executed) * </pre> * - * <p> - * In addition, at any stage, an item can be removed from the queue (for example, when the user - * cancels a job in the queue.) See the corresponding field for their exact meanings. + * <p> In addition, at any stage, an item can be removed from the queue (for + * example, when the user cancels a job in the queue.) See the corresponding + * field for their exact meanings. * * @author Kohsuke Kawaguchi */ @ExportedBean public class Queue extends ResourceController implements Saveable { + /** * Items that are waiting for its quiet period to pass. * - * <p> - * This consists of {@link Item}s that cannot be run yet - * because its time has not yet come. + * <p> This consists of {@link Item}s that cannot be run yet because its + * time has not yet come. */ private final Set<WaitingItem> waitingList = new TreeSet<WaitingItem>(); - /** - * {@link Task}s that can be built immediately - * but blocked because another build is in progress, - * required {@link Resource}s are not available, or otherwise blocked - * by {@link Task#isBuildBlocked()}. + * {@link Task}s that can be built immediately but blocked because another + * build is in progress, required {@link Resource}s are not available, or + * otherwise blocked by {@link Task#isBuildBlocked()}. */ private final ItemList<BlockedItem> blockedProjects = new ItemList<BlockedItem>(); - /** - * {@link Task}s that can be built immediately - * that are waiting for available {@link Executor}. - * This list is sorted in such a way that earlier items are built earlier. + * {@link Task}s that can be built immediately that are waiting for + * available {@link Executor}. This list is sorted in such a way that + * earlier items are built earlier. */ private final ItemList<BuildableItem> buildables = new ItemList<BuildableItem>(); - /** * {@link Task}s that are being handed over to the executor, but execution * has not started yet. @@ -148,26 +143,24 @@ public class Queue extends ResourceController implements Saveable { private final ItemList<BuildableItem> pendings = new ItemList<BuildableItem>(); /** - * Data structure created for each idle {@link Executor}. - * This is a job offer from the queue to an executor. + * Data structure created for each idle {@link Executor}. This is a job + * offer from the queue to an executor. * - * <p> - * An idle executor (that calls {@link Queue#pop()} creates - * a new {@link JobOffer} and gets itself {@linkplain Queue#parked parked}, - * and we'll eventually hand out an {@link #workUnit} to build. + * <p> An idle executor (that calls {@link Queue#pop()} creates a new + * {@link JobOffer} and gets itself {@linkplain Queue#parked parked}, and + * we'll eventually hand out an {@link #workUnit} to build. */ public class JobOffer extends MappingWorksheet.ExecutorSlot { - public final Executor executor; + public final Executor executor; /** - * Used to wake up an executor, when it has an offered - * {@link Project} to build. + * Used to wake up an executor, when it has an offered {@link Project} + * to build. */ private final OneShotEvent event = new OneShotEvent(Queue.this); - /** - * The work unit that this {@link Executor} is going to handle. - * (Or null, in which case event is used to trigger a queue maintenance.) + * The work unit that this {@link Executor} is going to handle. (Or + * null, in which case event is used to trigger a queue maintenance.) */ private WorkUnit workUnit; @@ -188,18 +181,22 @@ public class Queue extends ResourceController implements Saveable { } /** - * Verifies that the {@link Executor} represented by this object is capable of executing the given task. + * Verifies that the {@link Executor} represented by this object is + * capable of executing the given task. */ public boolean canTake(Task task) { Node node = getNode(); - if (node==null) return false; // this executor is about to die - - if(node.canTake(task)!=null) + if (node == null) { + return false; // this executor is about to die + } + if (node.canTake(task) != null) { return false; // this node is not able to take the task - - for (QueueTaskDispatcher d : QueueTaskDispatcher.all()) - if (d.canTake(node,task)!=null) + } + for (QueueTaskDispatcher d : QueueTaskDispatcher.all()) { + if (d.canTake(node, task) != null) { return false; + } + } return isAvailable(); } @@ -219,18 +216,15 @@ public class Queue extends ResourceController implements Saveable { return getNode().getMode() == Mode.NORMAL; } } - /** * The executors that are currently waiting for a job to run. */ - private final Map<Executor,JobOffer> parked = new HashMap<Executor,JobOffer>(); - + private final Map<Executor, JobOffer> parked = new HashMap<Executor, JobOffer>(); private volatile transient LoadBalancer loadBalancer; - private volatile transient QueueSorter sorter; public Queue(LoadBalancer loadBalancer) { - this.loadBalancer = loadBalancer.sanitize(); + this.loadBalancer = loadBalancer.sanitize(); // if all the executors are busy doing something, then the queue won't be maintained in // timely fashion, so use another thread to make sure it happens. new MaintainTask(this); @@ -241,7 +235,9 @@ public class Queue extends ResourceController implements Saveable { } public void setLoadBalancer(LoadBalancer loadBalancer) { - if(loadBalancer==null) throw new IllegalArgumentException(); + if (loadBalancer == null) { + throw new IllegalArgumentException(); + } this.loadBalancer = loadBalancer; } @@ -267,9 +263,10 @@ public class Queue extends ResourceController implements Saveable { String line; while ((line = in.readLine()) != null) { AbstractProject j = Hudson.getInstance().getItemByFullName(line, AbstractProject.class); - if (j != null) + if (j != null) { j.scheduleBuild(); - } + } + } } finally { IOUtils.closeQuietly(in); } @@ -283,12 +280,12 @@ public class Queue extends ResourceController implements Saveable { for (Object o : list) { if (o instanceof Task) { // backward compatibility - schedule((Task)o, 0); + schedule((Task) o, 0); } else if (o instanceof Item) { - Item item = (Item)o; - if(item.task==null) + Item item = (Item) o; + if (item.task == null) { continue; // botched persistence. throw this one away - + } maxId = Math.max(maxId, item.id); if (item instanceof WaitingItem) { waitingList.add((WaitingItem) item); @@ -322,15 +319,20 @@ public class Queue extends ResourceController implements Saveable { /** * Persists the queue contents to the disk. */ + @Override public synchronized void save() { - if(BulkChange.contains(this)) return; - + if (BulkChange.contains(this)) { + return; + } + // write out the tasks on the queue - ArrayList<Queue.Item> items = new ArrayList<Queue.Item>(); - for (Item item: getItems()) { - if(item.task instanceof TransientTask) continue; - items.add(item); - } + ArrayList<Queue.Item> items = new ArrayList<Queue.Item>(); + for (Item item : getItems()) { + if (item.task instanceof TransientTask) { + continue; + } + items.add(item); + } try { XmlFile queueFile = new XmlFile(XSTREAM, getXMLQueueFile()); @@ -342,14 +344,15 @@ public class Queue extends ResourceController implements Saveable { } /** - * Wipes out all the items currently in the queue, as if all of them are cancelled at once. + * Wipes out all the items currently in the queue, as if all of them are + * cancelled at once. */ - - @CLIMethod(name="clear-queue") + @CLIMethod(name = "clear-queue") public synchronized void clear() { Hudson.getInstance().checkPermission(Hudson.ADMINISTER); - for (WaitingItem i : waitingList) + for (WaitingItem i : waitingList) { i.onCancelled(); + } waitingList.clear(); blockedProjects.cancelAll(); buildables.cancelAll(); @@ -373,19 +376,17 @@ public class Queue extends ResourceController implements Saveable { } /** - * @deprecated as of 1.311 - * Use {@link #schedule(AbstractProject)} + * @deprecated as of 1.311 Use {@link #schedule(AbstractProject)} */ public boolean add(AbstractProject p) { - return schedule(p)!=null; + return schedule(p) != null; } /** * Schedule a new build for this project. * - * @return true if the project is actually added to the queue. - * false if the queue contained it and therefore the add() - * was noop + * @return true if the project is actually added to the queue. false if the + * queue contained it and therefore the add() was noop */ public WaitingItem schedule(AbstractProject p) { return schedule(p, p.getQuietPeriod()); @@ -394,46 +395,49 @@ public class Queue extends ResourceController implements Saveable { /** * Schedules a new build with a custom quiet period. * - * <p> - * Left for backward compatibility with <1.114. + * <p> Left for backward compatibility with <1.114. * * @since 1.105 - * @deprecated as of 1.311 - * Use {@link #schedule(Task, int)} + * @deprecated as of 1.311 Use {@link #schedule(Task, int)} */ public boolean add(AbstractProject p, int quietPeriod) { - return schedule(p, quietPeriod)!=null; + return schedule(p, quietPeriod) != null; } /** * Schedules an execution of a task. * - * @param actions - * These actions can be used for associating information scoped to a particular build, to - * the task being queued. Upon the start of the build, these {@link Action}s will be automatically - * added to the {@link Run} object, and hence avaialable to everyone. - * For the convenience of the caller, this list can contain null, and those will be silently ignored. + * @param actions These actions can be used for associating information + * scoped to a particular build, to the task being queued. Upon the start of + * the build, these {@link Action}s will be automatically added to the + * {@link Run} object, and hence avaialable to everyone. For the convenience + * of the caller, this list can contain null, and those will be silently + * ignored. * @since 1.311 - * @return - * null if this task is already in the queue and therefore the add operation was no-op. - * Otherwise indicates the {@link WaitingItem} object added, although the nature of the queue - * is that such {@link Item} only captures the state of the item at a particular moment, - * and by the time you inspect the object, some of its information can be already stale. + * @return null if this task is already in the queue and therefore the add + * operation was no-op. Otherwise indicates the {@link WaitingItem} object + * added, although the nature of the queue is that such {@link Item} only + * captures the state of the item at a particular moment, and by the time + * you inspect the object, some of its information can be already stale. * - * That said, one can still look at {@link WaitingItem#future}, {@link WaitingItem#id}, etc. + * That said, one can still look at + * {@link WaitingItem#future}, {@link WaitingItem#id}, etc. */ public synchronized WaitingItem schedule(Task p, int quietPeriod, List<Action> actions) { // remove nulls actions = new ArrayList<Action>(actions); for (Iterator<Action> itr = actions.iterator(); itr.hasNext();) { - Action a = itr.next(); - if (a==null) itr.remove(); + Action a = itr.next(); + if (a == null) { + itr.remove(); + } } - for(QueueDecisionHandler h : QueueDecisionHandler.all()) - if (!h.shouldSchedule(p, actions)) + for (QueueDecisionHandler h : QueueDecisionHandler.all()) { + if (!h.shouldSchedule(p, actions)) { return null; // veto - + } + } return scheduleInternal(p, quietPeriod, actions); } @@ -441,62 +445,65 @@ public class Queue extends ResourceController implements Saveable { * Schedules an execution of a task. * * @since 1.311 - * @return - * null if this task is already in the queue and therefore the add operation was no-op. - * Otherwise indicates the {@link WaitingItem} object added, although the nature of the queue - * is that such {@link Item} only captures the state of the item at a particular moment, - * and by the time you inspect the object, some of its information can be already stale. + * @return null if this task is already in the queue and therefore the add + * operation was no-op. Otherwise indicates the {@link WaitingItem} object + * added, although the nature of the queue is that such {@link Item} only + * captures the state of the item at a particular moment, and by the time + * you inspect the object, some of its information can be already stale. * - * That said, one can still look at {@link WaitingItem#future}, {@link WaitingItem#id}, etc. + * That said, one can still look at + * {@link WaitingItem#future}, {@link WaitingItem#id}, etc. */ private synchronized WaitingItem scheduleInternal(Task p, int quietPeriod, List<Action> actions) { Calendar due = new GregorianCalendar(); - due.add(Calendar.SECOND, quietPeriod); + due.add(Calendar.SECOND, quietPeriod); // Do we already have this task in the queue? Because if so, we won't schedule a new one. - List<Item> duplicatesInQueue = new ArrayList<Item>(); - for(Item item : getItems(p)) { - boolean shouldScheduleItem = false; - for (QueueAction action: item.getActions(QueueAction.class)) { + List<Item> duplicatesInQueue = new ArrayList<Item>(); + for (Item item : getItems(p)) { + boolean shouldScheduleItem = false; + for (QueueAction action : item.getActions(QueueAction.class)) { shouldScheduleItem |= action.shouldSchedule(actions); - } - for (QueueAction action: Util.filter(actions,QueueAction.class)) { + } + for (QueueAction action : Util.filter(actions, QueueAction.class)) { shouldScheduleItem |= action.shouldSchedule(item.getActions()); - } - if(!shouldScheduleItem) { - duplicatesInQueue.add(item); - } - } - if (duplicatesInQueue.isEmpty()) { - LOGGER.fine(p.getFullDisplayName() + " added to queue"); - - // put the item in the queue - WaitingItem added = new WaitingItem(due,p,actions); - waitingList.add(added); + } + if (!shouldScheduleItem) { + duplicatesInQueue.add(item); + } + } + if (duplicatesInQueue.isEmpty()) { + LOGGER.fine(p.getFullDisplayName() + " added to queue"); + + // put the item in the queue + WaitingItem added = new WaitingItem(due, p, actions); + waitingList.add(added); scheduleMaintenance(); // let an executor know that a new item is in the queue. return added; - } + } LOGGER.fine(p.getFullDisplayName() + " is already in the queue"); // but let the actions affect the existing stuff. - for(Item item : duplicatesInQueue) { - for(FoldableAction a : Util.filter(actions,FoldableAction.class)) { + for (Item item : duplicatesInQueue) { + for (FoldableAction a : Util.filter(actions, FoldableAction.class)) { a.foldIntoExisting(item, p, actions); } } boolean queueUpdated = false; - for(WaitingItem wi : Util.filter(duplicatesInQueue,WaitingItem.class)) { - if(quietPeriod<=0) { + for (WaitingItem wi : Util.filter(duplicatesInQueue, WaitingItem.class)) { + if (quietPeriod <= 0) { // the user really wants to build now, and they mean NOW. // so let's pull in the timestamp if we can. - if (wi.timestamp.before(due)) + if (wi.timestamp.before(due)) { continue; + } } else { // otherwise we do the normal quiet period implementation - if (wi.timestamp.after(due)) + if (wi.timestamp.after(due)) { continue; + } // quiet period timer reset. start the period over again } @@ -504,45 +511,46 @@ public class Queue extends ResourceController implements Saveable { waitingList.remove(wi); wi.timestamp = due; waitingList.add(wi); - queueUpdated=true; + queueUpdated = true; } - if (queueUpdated) scheduleMaintenance(); + if (queueUpdated) { + scheduleMaintenance(); + } return null; } - + /** - * @deprecated as of 1.311 - * Use {@link #schedule(Task, int)} + * @deprecated as of 1.311 Use {@link #schedule(Task, int)} */ public synchronized boolean add(Task p, int quietPeriod) { - return schedule(p, quietPeriod)!=null; + return schedule(p, quietPeriod) != null; } public synchronized WaitingItem schedule(Task p, int quietPeriod) { - return schedule(p, quietPeriod, new Action[0]); + return schedule(p, quietPeriod, new Action[0]); } /** - * @deprecated as of 1.311 - * Use {@link #schedule(Task, int, Action...)} + * @deprecated as of 1.311 Use {@link #schedule(Task, int, Action...)} */ public synchronized boolean add(Task p, int quietPeriod, Action... actions) { - return schedule(p, quietPeriod, actions)!=null; + return schedule(p, quietPeriod, actions) != null; } /** * Convenience wrapper method around {@link #schedule(Task, int, List)} */ public synchronized WaitingItem schedule(Task p, int quietPeriod, Action... actions) { - return schedule(p, quietPeriod, Arrays.asList(actions)); + return schedule(p, quietPeriod, Arrays.asList(actions)); } /** - * Cancels the item in the queue. If the item is scheduled more than once, cancels the first occurrence. + * Cancels the item in the queue. If the item is scheduled more than once, + * cancels the first occurrence. * * @return true if the project was indeed in the queue and was removed. - * false if this was no-op. + * false if this was no-op. */ public synchronized boolean cancel(Task p) { LOGGER.fine("Cancelling " + p.getFullDisplayName()); @@ -555,15 +563,16 @@ public class Queue extends ResourceController implements Saveable { } } // use bitwise-OR to make sure that both branches get evaluated all the time - return blockedProjects.cancel(p)!=null | buildables.cancel(p)!=null; + return blockedProjects.cancel(p) != null | buildables.cancel(p) != null; } - + public synchronized boolean cancel(Item item) { LOGGER.fine("Cancelling " + item.task.getFullDisplayName() + " item#" + item.id); // use bitwise-OR to make sure that all the branches get evaluated all the time boolean r = (item instanceof WaitingItem && waitingList.remove(item)) | blockedProjects.remove(item) | buildables.remove(item); - if(r) + if (r) { item.onCancelled(); + } return r; } @@ -578,33 +587,53 @@ public class Queue extends ResourceController implements Saveable { /** * Gets a snapshot of items in the queue. * - * Generally speaking the array is sorted such that the items that are most likely built sooner are - * at the end. + * Generally speaking the array is sorted such that the items that are most + * likely built sooner are at the end. */ - @Exported(inline=true) + @Exported(inline = true) public synchronized Item[] getItems() { Item[] r = new Item[waitingList.size() + blockedProjects.size() + buildables.size() + pendings.size()]; waitingList.toArray(r); int idx = waitingList.size(); - for (BlockedItem p : blockedProjects.values()) + for (BlockedItem p : blockedProjects.values()) { r[idx++] = p; - for (BuildableItem p : reverse(buildables.values())) + } + for (BuildableItem p : reverse(buildables.values())) { r[idx++] = p; - for (BuildableItem p : reverse(pendings.values())) + } + for (BuildableItem p : reverse(pendings.values())) { r[idx++] = p; + } return r; } - + public synchronized Item getItem(int id) { - for (Item item: waitingList) if (item.id == id) return item; - for (Item item: blockedProjects) if (item.id == id) return item; - for (Item item: buildables) if (item.id == id) return item; - for (Item item: pendings) if (item.id == id) return item; - return null; + for (Item item : waitingList) { + if (item.id == id) { + return item; + } + } + for (Item item : blockedProjects) { + if (item.id == id) { + return item; + } + } + for (Item item : buildables) { + if (item.id == id) { + return item; + } + } + for (Item item : pendings) { + if (item.id == id) { + return item; + } + } + return null; } /** - * Gets all the {@link BuildableItem}s that are waiting for an executor in the given {@link Computer}. + * Gets all the {@link BuildableItem}s that are waiting for an executor in + * the given {@link Computer}. */ public synchronized List<BuildableItem> getBuildableItems(Computer c) { List<BuildableItem> result = new ArrayList<BuildableItem>(); @@ -616,8 +645,9 @@ public class Queue extends ResourceController implements Saveable { private void _getBuildableItems(Computer c, ItemList<BuildableItem> col, List<BuildableItem> result) { Node node = c.getNode(); for (BuildableItem p : col.values()) { - if (node.canTake(p.task) == null) + if (node.canTake(p.task) == null) { result.add(p); + } } } @@ -641,9 +671,11 @@ public class Queue extends ResourceController implements Saveable { * Is the given task currently pending execution? */ public synchronized boolean isPending(Task t) { - for (BuildableItem i : pendings) - if (i.task.equals(t)) + for (BuildableItem i : pendings) { + if (i.task.equals(t)) { return true; + } + } return false; } @@ -666,12 +698,16 @@ public class Queue extends ResourceController implements Saveable { */ public synchronized int countBuildableItemsFor(Label l) { int r = 0; - for (BuildableItem bi : buildables.values()) - if(bi.task.getAssignedLabel()==l) + for (BuildableItem bi : buildables.values()) { + if (bi.task.getAssignedLabel() == l) { r++; - for (BuildableItem bi : pendings.values()) - if(bi.task.getAssignedLabel()==l) + } + } + for (BuildableItem bi : pendings.values()) { + if (bi.task.getAssignedLabel() == l) { r++; + } + } return r; } @@ -682,18 +718,22 @@ public class Queue extends ResourceController implements Saveable { */ public synchronized Item getItem(Task t) { BlockedItem bp = blockedProjects.get(t); - if (bp!=null) + if (bp != null) { return bp; + } BuildableItem bi = buildables.get(t); - if(bi!=null) + if (bi != null) { return bi; + } bi = pendings.get(t); - if(bi!=null) + if (bi != null) { return bi; + } for (Item item : waitingList) { - if (item.task == t) + if (item.task == t) { return item; + } } return null; } @@ -704,13 +744,14 @@ public class Queue extends ResourceController implements Saveable { * @return null if the project is not in the queue. */ public synchronized List<Item> getItems(Task t) { - List<Item> result =new ArrayList<Item>(); - result.addAll(blockedProjects.getAll(t)); - result.addAll(buildables.getAll(t)); + List<Item> result = new ArrayList<Item>(); + result.addAll(blockedProjects.getAll(t)); + result.addAll(buildables.getAll(t)); result.addAll(pendings.getAll(t)); for (Item item : waitingList) { - if (item.task == t) + if (item.task == t) { result.add(item); + } } return result; } @@ -718,29 +759,27 @@ public class Queue extends ResourceController implements Saveable { /** * Left for backward compatibility. * - * @see #getItem(Task) - public synchronized Item getItem(AbstractProject p) { - return getItem((Task) p); - } + * @see #getItem(Task) public synchronized Item getItem(AbstractProject p) { + * return getItem((Task) p); } */ - /** * Returns true if this queue contains the said project. */ public synchronized boolean contains(Task t) { - if (blockedProjects.containsKey(t) || buildables.containsKey(t) || pendings.containsKey(t)) + if (blockedProjects.containsKey(t) || buildables.containsKey(t) || pendings.containsKey(t)) { return true; + } for (Item item : waitingList) { - if (item.task == t) + if (item.task == t) { return true; + } } return false; } /** - * Called by the executor to fetch something to build next. - * <p> - * This method blocks until a next project becomes buildable. + * Called by the executor to fetch something to build next. <p> This method + * blocks until a next project becomes buildable. */ public synchronized WorkUnit pop() throws InterruptedException { final Executor exec = Executor.currentExecutor(); @@ -774,30 +813,34 @@ public class Queue extends ResourceController implements Saveable { // one last check to make sure this build is not blocked. if (isBuildBlocked(p.task)) { itr.remove(); - blockedProjects.put(p.task,new BlockedItem(p)); + blockedProjects.put(p.task, new BlockedItem(p)); continue; } List<JobOffer> candidates = new ArrayList<JobOffer>(parked.size()); - for (JobOffer j : parked.values()) - if(j.canTake(p.task)) + for (JobOffer j : parked.values()) { + if (j.canTake(p.task)) { candidates.add(j); + } + } MappingWorksheet ws = new MappingWorksheet(p, candidates); Mapping m = loadBalancer.map(p.task, ws); - if (m == null) - // if we couldn't find the executor that fits, - // just leave it in the buildables list and - // check if we can execute other projects + if (m == null) // if we couldn't find the executor that fits, + // just leave it in the buildables list and + // check if we can execute other projects + { continue; + } // found a matching executor. use it. WorkUnitContext wuc = new WorkUnitContext(p); m.execute(wuc); itr.remove(); - if (!wuc.getWorkUnits().isEmpty()) + if (!wuc.getWorkUnits().isEmpty()) { pendings.add(p); + } } // we went over all the buildable projects and awaken @@ -808,13 +851,16 @@ public class Queue extends ResourceController implements Saveable { if (!waitingList.isEmpty()) { // wait until the first item in the queue is due sleep = peek().timestamp.getTimeInMillis() - new GregorianCalendar().getTimeInMillis(); - if (sleep < 100) sleep = 100; // avoid wait(0) + if (sleep < 100) { + sleep = 100; // avoid wait(0) + } } - if (sleep == -1) + if (sleep == -1) { offer.event.block(); - else + } else { offer.event.block(sleep); + } // retract the offer object assert parked.get(exec) == offer; @@ -826,8 +872,9 @@ public class Queue extends ResourceController implements Saveable { LOGGER.fine("Pop returning " + offer.workUnit + " for " + exec.getName()); // TODO: I think this has to be done by the last executor that leaves the pop(), not by main executor - if (offer.workUnit.isMainWork()) + if (offer.workUnit.isMainWork()) { pendings.remove(offer.workUnit.context.item); + } return offer.workUnit; } @@ -852,10 +899,8 @@ public class Queue extends ResourceController implements Saveable { /** * Checks the queue and runs anything that can be run. * - * <p> - * When conditions are changed, this method should be invoked. - * <p> - * This wakes up one {@link Executor} so that it will maintain a queue. + * <p> When conditions are changed, this method should be invoked. <p> This + * wakes up one {@link Executor} so that it will maintain a queue. */ public synchronized void scheduleMaintenance() { // this code assumes that after this method is called @@ -877,13 +922,14 @@ public class Queue extends ResourceController implements Saveable { } /** - * Make sure we don't queue two tasks of the same project to be built - * unless that project allows concurrent builds. + * Make sure we don't queue two tasks of the same project to be built unless + * that project allows concurrent builds. */ private boolean allowNewBuildableTask(Task t) { try { - if (t.isConcurrentBuild()) + if (t.isConcurrentBuild()) { return true; + } } catch (AbstractMethodError e) { // earlier versions don't have the "isConcurrentBuild" method, so fall back gracefully } @@ -891,14 +937,14 @@ public class Queue extends ResourceController implements Saveable { } /** - * Queue maintenance. - * <p> - * Move projects between {@link #waitingList}, {@link #blockedProjects}, and {@link #buildables} + * Queue maintenance. <p> Move projects between + * {@link #waitingList}, {@link #blockedProjects}, and {@link #buildables} * appropriately. */ public synchronized void maintain() { - if (LOGGER.isLoggable(Level.FINE)) + if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Queue maintenance started " + this); + } // blocked -> buildable Iterator<BlockedItem> itr = blockedProjects.values().iterator(); @@ -915,9 +961,9 @@ public class Queue extends ResourceController implements Saveable { while (!waitingList.isEmpty()) { WaitingItem top = peek(); - if (!top.timestamp.before(new GregorianCalendar())) + if (!top.timestamp.before(new GregorianCalendar())) { return; // finished moving all ready items from queue - + } waitingList.remove(top); Task p = top.task; if (!isBuildBlocked(p) && allowNewBuildableTask(p)) { @@ -928,32 +974,38 @@ public class Queue extends ResourceController implements Saveable { // this can't be built now because another build is in progress // set this project aside. LOGGER.fine(p.getFullDisplayName() + " is blocked"); - blockedProjects.put(p,new BlockedItem(top)); + blockedProjects.put(p, new BlockedItem(top)); } } final QueueSorter s = sorter; - if (s != null) - s.sortBuildableItems(buildables); + if (s != null) { + s.sortBuildableItems(buildables); + } } private void makeBuildable(BuildableItem p) { - if(Hudson.FLYWEIGHT_SUPPORT && p.task instanceof FlyweightTask && !ifBlockedByHudsonShutdown(p.task)) { + if (Hudson.FLYWEIGHT_SUPPORT && p.task instanceof FlyweightTask && !ifBlockedByHudsonShutdown(p.task)) { ConsistentHash<Node> hash = new ConsistentHash<Node>(new Hash<Node>() { public String hash(Node node) { return node.getNodeName(); } }); Hudson h = Hudson.getInstance(); - hash.add(h, h.getNumExecutors()*100); - for (Node n : h.getNodes()) - hash.add(n,n.getNumExecutors()*100); + hash.add(h, h.getNumExecutors() * 100); + for (Node n : h.getNodes()) { + hash.add(n, n.getNumExecutors() * 100); + } Label lbl = p.task.getAssignedLabel(); for (Node n : hash.list(p.task.getFullDisplayName())) { Computer c = n.toComputer(); - if (c==null || c.isOffline()) continue; - if (lbl!=null && !lbl.contains(n)) continue; + if (c == null || c.isOffline()) { + continue; + } + if (lbl != null && !lbl.contains(n)) { + continue; + } c.startFlyWeightTask(new WorkUnitContext(p).createWorkUnit(p.task)); pendings.add(p); return; @@ -961,8 +1013,8 @@ public class Queue extends ResourceController implements Saveable { // if the execution get here, it means we couldn't schedule it anywhere. // so do the scheduling like other normal jobs. } - - buildables.put(p.task,p); + + buildables.put(p.task, p); } public static boolean ifBlockedByHudsonShutdown(Task task) { @@ -975,74 +1027,75 @@ public class Queue extends ResourceController implements Saveable { /** * Marks {@link Task}s that are not persisted. + * * @since 1.311 */ - public interface TransientTask extends Task {} + public interface TransientTask extends Task { + } /** * Marks {@link Task}s that do not consume {@link Executor}. + * * @see OneOffExecutor * @since 1.318 */ - public interface FlyweightTask extends Task {} + public interface FlyweightTask extends Task { + } /** - * Marks {@link Task}s that are not affected by the {@linkplain Hudson#isQuietingDown()} quieting down}, - * because these tasks keep other tasks executing. + * Marks {@link Task}s that are not affected by the + * {@linkplain Hudson#isQuietingDown()} quieting down}, because these tasks + * keep other tasks executing. * - * @since 1.336 + * @since 1.336 */ - public interface NonBlockingTask extends Task {} + public interface NonBlockingTask extends Task { + } /** * Task whose execution is controlled by the queue. * - * <p> - * {@link #equals(Object) Value equality} of {@link Task}s is used - * to collapse two tasks into one. This is used to avoid infinite - * queue backlog. + * <p> {@link #equals(Object) Value equality} of {@link Task}s is used to + * collapse two tasks into one. This is used to avoid infinite queue + * backlog. * - * <p> - * Pending {@link Task}s are persisted when Hudson shuts down, so - * it needs to be persistable via XStream. To create a non-persisted - * transient Task, extend {@link TransientTask} marker interface. + * <p> Pending {@link Task}s are persisted when Hudson shuts down, so it + * needs to be persistable via XStream. To create a non-persisted transient + * Task, extend {@link TransientTask} marker interface. * - * <p> - * Plugins are encouraged to extend from {@link AbstractQueueTask} + * <p> Plugins are encouraged to extend from {@link AbstractQueueTask} * instead of implementing this interface directly, to maintain * compatibility with future changes to this interface. * - * <p> - * For historical reasons, {@link Task} object by itself - * also represents the "primary" sub-task (and as implied by this - * design, a {@link Task} must have at least one sub-task.) - * Most of the time, the primary subtask is the only sub task. + * <p> For historical reasons, {@link Task} object by itself also represents + * the "primary" sub-task (and as implied by this design, a {@link Task} + * must have at least one sub-task.) Most of the time, the primary subtask + * is the only sub task. */ public interface Task extends ModelObject, SubTask { + /** - * Returns true if the execution should be blocked - * for temporary reasons. + * Returns true if the execution should be blocked for temporary + * reasons. * - * <p> - * Short-hand for {@code getCauseOfBlockage()!=null}. + * <p> Short-hand for {@code getCauseOfBlockage()!=null}. */ boolean isBuildBlocked(); /** - * @deprecated as of 1.330 - * Use {@link CauseOfBlockage#getShortDescription()} instead. + * @deprecated as of 1.330 Use + * {@link CauseOfBlockage#getShortDescription()} instead. */ String getWhyBlocked(); /** - * If the execution of this task should be blocked for temporary reasons, - * this method returns a non-null object explaining why. + * If the execution of this task should be blocked for temporary + * reasons, this method returns a non-null object explaining why. * - * <p> - * Otherwise this method returns null, indicating that the build can proceed right away. + * <p> Otherwise this method returns null, indicating that the build can + * proceed right away. * - * <p> - * This can be used to define mutual exclusion that goes beyond + * <p> This can be used to define mutual exclusion that goes beyond * {@link #getResourceList()}. */ CauseOfBlockage getCauseOfBlockage(); @@ -1050,8 +1103,8 @@ public class Queue extends ResourceController implements Saveable { /** * Unique name of this task. * - * <p> - * This method is no longer used, left here for compatibility. Just return {@link #getDisplayName()}. + * <p> This method is no longer used, left here for compatibility. Just + * return {@link #getDisplayName()}. */ String getName(); @@ -1061,31 +1114,32 @@ public class Queue extends ResourceController implements Saveable { String getFullDisplayName(); /** - * Checks the permission to see if the current user can abort this executable. - * Returns normally from this method if it's OK. + * Checks the permission to see if the current user can abort this + * executable. Returns normally from this method if it's OK. * - * @throws org.acegisecurity.AccessDeniedException if the permission is not granted. + * @throws org.acegisecurity.AccessDeniedException if the permission is + * not granted. */ void checkAbortPermission(); /** - * Works just like {@link #checkAbortPermission()} except it indicates the status by a return value, - * instead of exception. + * Works just like {@link #checkAbortPermission()} except it indicates + * the status by a return value, instead of exception. */ boolean hasAbortPermission(); - + /** - * Returns the URL of this task relative to the context root of the application. + * Returns the URL of this task relative to the context root of the + * application. * - * <p> - * When the user clicks an item in the queue, this is the page where the user is taken to. - * Hudson expects the current instance to be bound to the URL returned by this method. + * <p> When the user clicks an item in the queue, this is the page where + * the user is taken to. Hudson expects the current instance to be bound + * to the URL returned by this method. * - * @return - * URL that ends with '/'. + * @return URL that ends with '/'. */ String getUrl(); - + /** * True if the task allows concurrent builds * @@ -1096,17 +1150,15 @@ public class Queue extends ResourceController implements Saveable { /** * Obtains the {@link SubTask}s that constitute this task. * - * <p> - * The collection returned by this method must also contain the primary {@link SubTask} - * represented by this {@link Task} object itself as the first element. - * The returned value is read-only. + * <p> The collection returned by this method must also contain the + * primary {@link SubTask} represented by this {@link Task} object + * itself as the first element. The returned value is read-only. * - * <p> - * At least size 1. + * <p> At least size 1. * - * <p> - * Since this is a newly added method, the invocation may results in {@link AbstractMethodError}. - * Use {@link Tasks#getSubTasksOf(Task)} that avoids this. + * <p> Since this is a newly added method, the invocation may results in + * {@link AbstractMethodError}. Use {@link Tasks#getSubTasksOf(Task)} + * that avoids this. * * @since 1.377 */ @@ -1116,34 +1168,34 @@ public class Queue extends ResourceController implements Saveable { /** * Represents the real meat of the computation run by {@link Executor}. * - * <h2>Views</h2> - * <p> - * Implementation must have <tt>executorCell.jelly</tt>, which is - * used to render the HTML that indicates this executable is executing. + * <h2>Views</h2> <p> Implementation must have <tt>executorCell.jelly</tt>, + * which is used to render the HTML that indicates this executable is + * executing. */ public interface Executable extends Runnable { + /** - * Task from which this executable was created. - * Never null. + * Task from which this executable was created. Never null. * - * <p> - * Since this method went through a signature change in 1.377, the invocation may results in - * {@link AbstractMethodError}. - * Use {@link Executables#getParentOf(Executable)} that avoids this. + * <p> Since this method went through a signature change in 1.377, the + * invocation may results in {@link AbstractMethodError}. Use + * {@link Executables#getParentOf(Executable)} that avoids this. */ SubTask getParent(); /** * Called by {@link Executor} to perform the task */ + @Override void run(); - + /** * Estimate of how long will it take to execute this executable. * Measured in milliseconds. - * - * Please, consider using {@link Executables#getEstimatedDurationFor(Executable)} - * to protected against AbstractMethodErrors! + * + * Please, consider using + * {@link Executables#getEstimatedDurationFor(Executable)} to protected + * against AbstractMethodErrors! * * @return -1 if it's impossible to estimate. * @since 1.383 @@ -1151,9 +1203,11 @@ public class Queue extends ResourceController implements Saveable { long getEstimatedDuration(); /** - * Used to render the HTML. Should be a human readable text of what this executable is. + * Used to render the HTML. Should be a human readable text of what this + * executable is. */ - @Override String toString(); + @Override + String toString(); } /** @@ -1161,61 +1215,71 @@ public class Queue extends ResourceController implements Saveable { */ @ExportedBean(defaultVisibility = 999) public static abstract class Item extends Actionable { + /** - * VM-wide unique ID that tracks the {@link Task} as it moves through different stages - * in the queue (each represented by different subtypes of {@link Item}. + * VM-wide unique ID that tracks the {@link Task} as it moves through + * different stages in the queue (each represented by different subtypes + * of {@link Item}. */ //TODO: review and check whether we can do it private - public final int id; - - /** + public final int id; + /** * Project to be built. */ //TODO: review and check whether we can do it private @Exported public final Task task; - private /*almost final*/ transient FutureImpl future; /** - * Build is blocked because another build is in progress, - * required {@link Resource}s are not available, or otherwise blocked - * by {@link Task#isBuildBlocked()}. + * Build is blocked because another build is in progress, required + * {@link Resource}s are not available, or otherwise blocked by + * {@link Task#isBuildBlocked()}. */ @Exported - public boolean isBlocked() { return this instanceof BlockedItem; } + public boolean isBlocked() { + return this instanceof BlockedItem; + } /** - * Build is waiting the executor to become available. - * This flag is only used in {@link Queue#getItems()} for - * 'pseudo' items that are actually not really in the queue. + * Build is waiting the executor to become available. This flag is only + * used in {@link Queue#getItems()} for 'pseudo' items that are actually + * not really in the queue. */ @Exported - public boolean isBuildable() { return this instanceof BuildableItem; } + public boolean isBuildable() { + return this instanceof BuildableItem; + } /** * True if the item is starving for an executor for too long. */ @Exported - public boolean isStuck() { return false; } + public boolean isStuck() { + return false; + } /** - * Can be used to wait for the completion (either normal, abnormal, or cancellation) of the {@link Task}. - * <p> - * Just like {@link #id}, the same object tracks various stages of the queue. + * Can be used to wait for the completion (either normal, abnormal, or + * cancellation) of the {@link Task}. <p> Just like {@link #id}, the + * same object tracks various stages of the queue. */ - public Future<Executable> getFuture() { return future; } + public Future<Executable> getFuture() { + return future; + } /** - * Convenience method that returns a read only view of the {@link Cause}s associated with this item in the queue. + * Convenience method that returns a read only view of the + * {@link Cause}s associated with this item in the queue. * * @return can be empty but never null * @since 1.343 */ public final List<Cause> getCauses() { CauseAction ca = getAction(CauseAction.class); - if (ca!=null) + if (ca != null) { return Collections.unmodifiableList(ca.getCauses()); + } return Collections.emptyList(); } @@ -1223,11 +1287,13 @@ public class Queue extends ResourceController implements Saveable { this.task = task; this.id = id; this.future = future; - for (Action action: actions) addAction(action); + for (Action action : actions) { + addAction(action); + } } - + protected Item(Item item) { - this(item.task, item.getActions(), item.id, item.future); + this(item.task, item.getActions(), item.id, item.future); } public int getId() { @@ -1239,12 +1305,13 @@ public class Queue extends ResourceController implements Saveable { } /** - * Gets a human-readable status message describing why it's in the queue. + * Gets a human-readable status message describing why it's in the + * queue. */ @Exported public final String getWhy() { CauseOfBlockage cob = getCauseOfBlockage(); - return cob!=null ? cob.getShortDescription() : null; + return cob != null ? cob.getShortDescription() : null; } /** @@ -1255,46 +1322,50 @@ public class Queue extends ResourceController implements Saveable { /** * Gets a human-readable message about the parameters of this item + * * @return String */ @Exported public String getParams() { - StringBuilder s = new StringBuilder(); - for(Action action : getActions()) { - if(action instanceof ParametersAction) { - ParametersAction pa = (ParametersAction)action; - for (ParameterValue p : pa.getParameters()) { - s.append('\n').append(p.getShortDescription()); - } - } - } - return s.toString(); - } - + StringBuilder s = new StringBuilder(); + for (Action action : getActions()) { + if (action instanceof ParametersAction) { + ParametersAction pa = (ParametersAction) action; + for (ParameterValue p : pa.getParameters()) { + s.append('\n').append(p.getShortDescription()); + } + } + } + return s.toString(); + } + public boolean hasCancelPermission() { return task.hasAbortPermission(); } - + + @Override public String getDisplayName() { - // TODO Auto-generated method stub - return null; - } + // TODO Auto-generated method stub + return null; + } - public String getSearchUrl() { - // TODO Auto-generated method stub - return null; - } + @Override + public String getSearchUrl() { + // TODO Auto-generated method stub + return null; + } /** * Called from queue.jelly. */ public HttpResponse doCancelQueue() throws IOException, ServletException { - Hudson.getInstance().getQueue().cancel(this); + Hudson.getInstance().getQueue().cancel(this); return HttpResponses.forwardToPreviousPage(); } /** - * Participates in the cancellation logic to set the {@link #future} accordingly. + * Participates in the cancellation logic to set the {@link #future} + * accordingly. */ /*package*/ void onCancelled() { future.setAsCancelled(); @@ -1307,59 +1378,62 @@ public class Queue extends ResourceController implements Saveable { @Override public String toString() { - return getClass().getName()+':'+task.toString(); + return getClass().getName() + ':' + task.toString(); } } - + /** - * An optional interface for actions on Queue.Item. - * Lets the action cooperate in queue management. - * + * An optional interface for actions on Queue.Item. Lets the action + * cooperate in queue management. + * * @since 1.300-ish. */ public interface QueueAction extends Action { - /** - * Returns whether the new item should be scheduled. - * An action should return true if the associated task is 'different enough' to warrant a separate execution. - */ - public boolean shouldSchedule(List<Action> actions); + + /** + * Returns whether the new item should be scheduled. An action should + * return true if the associated task is 'different enough' to warrant a + * separate execution. + */ + public boolean shouldSchedule(List<Action> actions); } /** - * Extension point for deciding if particular job should be scheduled or not. + * Extension point for deciding if particular job should be scheduled or + * not. * - * <p> - * This handler is consulted every time someone tries to submit a task to the queue. - * If any of the registered handlers returns false, the task will not be added - * to the queue, and the task will never get executed. + * <p> This handler is consulted every time someone tries to submit a task + * to the queue. If any of the registered handlers returns false, the task + * will not be added to the queue, and the task will never get executed. * - * <p> - * This extension point is still a subject to change, as we are seeking more - * comprehensive Queue pluggability. See HUDSON-2072. + * <p> This extension point is still a subject to change, as we are seeking + * more comprehensive Queue pluggability. See HUDSON-2072. * * @since 1.316 */ public static abstract class QueueDecisionHandler implements ExtensionPoint { - /** - * Returns whether the new item should be scheduled. - */ - public abstract boolean shouldSchedule(Task p, List<Action> actions); - - /** - * All registered {@link QueueDecisionHandler}s - * @return - */ - public static ExtensionList<QueueDecisionHandler> all() { - return Hudson.getInstance().getExtensionList(QueueDecisionHandler.class); - } + + /** + * Returns whether the new item should be scheduled. + */ + public abstract boolean shouldSchedule(Task p, List<Action> actions); + + /** + * All registered {@link QueueDecisionHandler}s + * + * @return + */ + public static ExtensionList<QueueDecisionHandler> all() { + return Hudson.getInstance().getExtensionList(QueueDecisionHandler.class); + } } - + /** * {@link Item} in the {@link Queue#waitingList} stage. */ public static final class WaitingItem extends Item implements Comparable<WaitingItem> { - private static final AtomicInteger COUNTER = new AtomicInteger(0); - + + private static final AtomicInteger COUNTER = new AtomicInteger(0); /** * This item can be run after this time. */ @@ -1375,20 +1449,25 @@ public class Queue extends ResourceController implements Saveable { super(project, actions, COUNTER.incrementAndGet(), new FutureImpl(project)); this.timestamp = timestamp; } - + + @Override public int compareTo(WaitingItem that) { int r = this.timestamp.getTime().compareTo(that.timestamp.getTime()); - if (r != 0) return r; + if (r != 0) { + return r; + } return this.id - that.id; } + @Override public CauseOfBlockage getCauseOfBlockage() { long diff = timestamp.getTimeInMillis() - System.currentTimeMillis(); - if (diff > 0) + if (diff > 0) { return CauseOfBlockage.fromMessage(Messages._Queue_InQuietPeriod(Util.getTimeSpanString(diff))); - else + } else { return CauseOfBlockage.fromMessage(Messages._Queue_Unknown()); + } } } @@ -1396,6 +1475,7 @@ public class Queue extends ResourceController implements Saveable { * Common part between {@link BlockedItem} and {@link BuildableItem}. */ public static abstract class NotWaitingItem extends Item { + /** * When did this job exit the {@link Queue#waitingList} phase? */ @@ -1417,6 +1497,7 @@ public class Queue extends ResourceController implements Saveable { * {@link Item} in the {@link Queue#blockedProjects} stage. */ public final class BlockedItem extends NotWaitingItem { + public BlockedItem(WaitingItem wi) { super(wi); } @@ -1429,7 +1510,9 @@ public class Queue extends ResourceController implements Saveable { ResourceActivity r = getBlockingActivity(task); if (r != null) { if (r == task) // blocked by itself, meaning another build is in progress + { return CauseOfBlockage.fromMessage(Messages._Queue_InProgress()); + } return CauseOfBlockage.fromMessage(Messages._Queue_BlockedBy(r.getDisplayName())); } return task.getCauseOfBlockage(); @@ -1440,6 +1523,7 @@ public class Queue extends ResourceController implements Saveable { * {@link Item} in the {@link Queue#buildables} stage. */ public final static class BuildableItem extends NotWaitingItem { + public BuildableItem(WaitingItem wi) { super(wi); } @@ -1448,52 +1532,60 @@ public class Queue extends ResourceController implements Saveable { super(ni); } + @Override public CauseOfBlockage getCauseOfBlockage() { Hudson hudson = Hudson.getInstance(); - if(ifBlockedByHudsonShutdown(task)) + if (ifBlockedByHudsonShutdown(task)) { return CauseOfBlockage.fromMessage(Messages._Queue_HudsonIsAboutToShutDown()); + } Label label = task.getAssignedLabel(); - if (hudson.getNodes().isEmpty()) + if (hudson.getNodes().isEmpty()) { label = null; // no master/slave. pointless to talk about nodes - + } if (label != null) { if (label.isOffline()) { Set<Node> nodes = label.getNodes(); - if (nodes.size() != 1) return new BecauseLabelIsOffline(label); - else return new BecauseNodeIsOffline(nodes.iterator().next()); + if (nodes.size() != 1) { + return new BecauseLabelIsOffline(label); + } else { + return new BecauseNodeIsOffline(nodes.iterator().next()); + } } } - if(label==null) + if (label == null) { return CauseOfBlockage.fromMessage(Messages._Queue_WaitingForNextAvailableExecutor()); + } Set<Node> nodes = label.getNodes(); - if (nodes.size() != 1) return new BecauseLabelIsBusy(label); - else return new BecauseNodeIsBusy(nodes.iterator().next()); + if (nodes.size() != 1) { + return new BecauseLabelIsBusy(label); + } else { + return new BecauseNodeIsBusy(nodes.iterator().next()); + } } @Override public boolean isStuck() { Label label = task.getAssignedLabel(); - if(label!=null && label.isOffline()) - // no executor online to process this job. definitely stuck. + if (label != null && label.isOffline()) // no executor online to process this job. definitely stuck. + { return true; + } long d = task.getEstimatedDuration(); - long elapsed = System.currentTimeMillis()-buildableStartMilliseconds; - if(d>=0) { + long elapsed = System.currentTimeMillis() - buildableStartMilliseconds; + if (d >= 0) { // if we were running elsewhere, we would have done this build ten times. - return elapsed > Math.max(d,60000L)*10; + return elapsed > Math.max(d, 60000L) * 10; } else { // more than a day in the queue - return TimeUnit2.MILLISECONDS.toHours(elapsed)>24; + return TimeUnit2.MILLISECONDS.toHours(elapsed) > 24; } } } - private static final Logger LOGGER = Logger.getLogger(Queue.class.getName()); - /** * This {@link XStream} instance is used to persist {@link Task}s. */ @@ -1501,50 +1593,54 @@ public class Queue extends ResourceController implements Saveable { static { XSTREAM.registerConverter(new AbstractSingleValueConverter() { + @Override + @SuppressWarnings("unchecked") + public boolean canConvert(Class klazz) { + return hudson.model.Item.class.isAssignableFrom(klazz); + } - @Override - @SuppressWarnings("unchecked") - public boolean canConvert(Class klazz) { - return hudson.model.Item.class.isAssignableFrom(klazz); - } - - @Override - public Object fromString(String string) { + @Override + public Object fromString(String string) { Object item = Hudson.getInstance().getItemByFullName(string); - if(item==null) throw new NoSuchElementException("No such job exists: "+string); + if (item == null) { + throw new NoSuchElementException("No such job exists: " + string); + } return item; - } + } - @Override - public String toString(Object item) { - return ((hudson.model.Item) item).getFullName(); - } + @Override + public String toString(Object item) { + return ((hudson.model.Item) item).getFullName(); + } }); XSTREAM.registerConverter(new AbstractSingleValueConverter() { + @SuppressWarnings("unchecked") + @Override + public boolean canConvert(Class klazz) { + return Run.class.isAssignableFrom(klazz); + } - @SuppressWarnings("unchecked") - @Override - public boolean canConvert(Class klazz) { - return Run.class.isAssignableFrom(klazz); - } - - @Override - public Object fromString(String string) { - String[] split = string.split("#"); - String projectName = split[0]; - int buildNumber = Integer.parseInt(split[1]); - Job<?,?> job = (Job<?,?>) Hudson.getInstance().getItemByFullName(projectName); - if(job==null) throw new NoSuchElementException("No such job exists: "+projectName); - Run<?,?> run = job.getBuildByNumber(buildNumber); - if(run==null) throw new NoSuchElementException("No such build: "+string); - return run; - } - - @Override - public String toString(Object object) { - Run<?,?> run = (Run<?,?>) object; - return run.getParent().getFullName() + "#" + run.getNumber(); - } + @Override + public Object fromString(String string) { + String[] split = string.split("#"); + String projectName = split[0]; + int buildNumber = Integer.parseInt(split[1]); + Job<?, ?> job = (Job<?, ?>) Hudson.getInstance().getItemByFullName(projectName); + if (job == null) { + throw new NoSuchElementException("No such job exists: " + projectName); + } + Run<?, ?> run = job.getBuildByNumber(buildNumber); + if (run == null) { + throw new NoSuchElementException("No such build: " + string); + } + return run; + } + + @Override + public String toString(Object object) { + Run<?, ?> run = (Run<?, ?>) object; + return run.getParent().getFullName() + "#" + run.getNumber(); + } }); } @@ -1553,6 +1649,7 @@ public class Queue extends ResourceController implements Saveable { * {@link Queue} gets GC-ed. */ private static class MaintainTask extends SafeTimerTask { + private final WeakReference<Queue> queue; MaintainTask(Queue queue) { @@ -1562,84 +1659,94 @@ public class Queue extends ResourceController implements Saveable { Trigger.timer.schedule(this, interval, interval); } + @Override protected void doRun() { Queue q = queue.get(); - if (q != null) + if (q != null) { q.maintain(); - else + } else { cancel(); + } } } - + /** * {@link ArrayList} of {@link Item} with more convenience methods. */ private static class ItemList<T extends Item> extends ArrayList<T> { - public T get(Task task) { - for (T item: this) { - if (item.task == task) { - return item; - } - } - return null; - } - - public List<T> getAll(Task task) { - List<T> result = new ArrayList<T>(); - for (T item: this) { - if (item.task == task) { - result.add(item); - } - } - return result; - } - - public boolean containsKey(Task task) { - return get(task) != null; - } - - public T remove(Task task) { - Iterator<T> it = iterator(); - while (it.hasNext()) { - T t = it.next(); - if (t.task == task) { - it.remove(); - return t; - } - } - return null; - } - - public void put(Task task, T item) { - assert item.task == task; - add(item); - } - - public ItemList<T> values() { - return this; - } + + public T get(Task task) { + for (T item : this) { + if (item.task == task) { + return item; + } + } + return null; + } + + public List<T> getAll(Task task) { + List<T> result = new ArrayList<T>(); + for (T item : this) { + if (item.task == task) { + result.add(item); + } + } + return result; + } + + public boolean containsKey(Task task) { + return get(task) != null; + } + + public T remove(Task task) { + Iterator<T> it = iterator(); + while (it.hasNext()) { + T t = it.next(); + if (t.task == task) { + it.remove(); + return t; + } + } + return null; + } + + public void put(Task task, T item) { + assert item.task == task; + add(item); + } + + public ItemList<T> values() { + return this; + } /** - * Works like {@link #remove(Task)} but also marks the {@link Item} as cancelled. + * Works like {@link #remove(Task)} but also marks the {@link Item} as + * cancelled. */ public T cancel(Task p) { T x = remove(p); - if(x!=null) x.onCancelled(); + if (x != null) { + x.onCancelled(); + } return x; } /** - * Works like {@link #remove(Object)} but also marks the {@link Item} as cancelled. + * Works like {@link #remove(Object)} but also marks the {@link Item} as + * cancelled. */ public boolean cancel(Item t) { boolean r = remove(t); - if(r) t.onCancelled(); + if (r) { + t.onCancelled(); + } return r; } public void cancelAll() { - for (T t : this) + for (T t : this) { t.onCancelled(); + } clear(); } } @@ -1652,7 +1759,7 @@ public class Queue extends ResourceController implements Saveable { /** * Restores the queue content during the start up. */ - @Initializer(after=JOB_LOADED) + @Initializer(after = JOB_LOADED) public static void init(Hudson h) { h.getQueue().load(); } diff --git a/hudson-core/src/main/java/hudson/model/Run.java b/hudson-core/src/main/java/hudson/model/Run.java index 5c87d50..4233ab2 100644 --- a/hudson-core/src/main/java/hudson/model/Run.java +++ b/hudson-core/src/main/java/hudson/model/Run.java @@ -7,10 +7,10 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * - * Contributors: + * Contributors: * * Kohsuke Kawaguchi, Daniel Dyer, Red Hat, Inc., Tom Huybrechts, Romain Seguy, Yahoo! Inc., Darek Ostolski - * + * * *******************************************************************************/ @@ -106,76 +106,69 @@ import static java.util.logging.Level.FINE; /** * A particular execution of {@link Job}. * - * <p> - * Custom {@link Run} type is always used in conjunction with - * a custom {@link Job} type, so there's no separate registration - * mechanism for custom {@link Run} types. + * <p> Custom {@link Run} type is always used in conjunction with a custom + * {@link Job} type, so there's no separate registration mechanism for custom + * {@link Run} types. * * @author Kohsuke Kawaguchi * @see RunListener */ @ExportedBean -public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,RunT>> +public abstract class Run<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, RunT>> extends Actionable implements ExtensionPoint, Comparable<RunT>, AccessControlled, PersistenceRoot, DescriptorByNameOwner { protected transient final JobT project; - /** * Build number. * - * <p> - * In earlier versions < 1.24, this number is not unique nor continuous, - * but going forward, it will, and this really replaces the build id. + * <p> In earlier versions < 1.24, this number is not unique nor + * continuous, but going forward, it will, and this really replaces the + * build id. */ public /*final*/ int number; - /** - * Previous build. Can be null. - * These two fields are maintained and updated by {@link RunMap}. + * Previous build. Can be null. These two fields are maintained and updated + * by {@link RunMap}. */ protected volatile transient RunT previousBuild; - /** * Next build. Can be null. */ protected volatile transient RunT nextBuild; - /** - * Pointer to the next younger build in progress. This data structure is lazily updated, - * so it may point to the build that's already completed. This pointer is set to 'this' - * if the computation determines that everything earlier than this build is already completed. + * Pointer to the next younger build in progress. This data structure is + * lazily updated, so it may point to the build that's already completed. + * This pointer is set to 'this' if the computation determines that + * everything earlier than this build is already completed. */ /* does not compile on JDK 7: private*/ volatile transient RunT previousBuildInProgress; - /** * When the build is scheduled. */ protected transient final long timestamp; - /** - * The build result. - * This value may change while the state is in {@link State#BUILDING}. + * The build result. This value may change while the state is in + * {@link State#BUILDING}. */ protected volatile Result result; - /** * Human-readable description. Can be null. */ protected volatile String description; - /** - * Human-readable name of this build. Can be null. - * If non-null, this text is displayed instead of "#NNN", which is the default. + * Human-readable name of this build. Can be null. If non-null, this text is + * displayed instead of "#NNN", which is the default. + * * @since 1.390 */ private volatile String displayName; - /** * The current build state. */ protected volatile transient State state; private static enum State { + /** * Build is created/queued but we haven't started building it. */ @@ -185,8 +178,8 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run */ BUILDING, /** - * Build is completed now, and the status is determined, - * but log files are still being updated. + * Build is completed now, and the status is determined, but log files + * are still being updated. */ POST_PRODUCTION, /** @@ -194,33 +187,28 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run */ COMPLETED } - /** * Number of milli-seconds it took to run this build. */ protected long duration; - /** - * Charset in which the log file is written. - * For compatibility reason, this field may be null. - * For persistence, this field is string and not {@link Charset}. + * Charset in which the log file is written. For compatibility reason, this + * field may be null. For persistence, this field is string and not + * {@link Charset}. * * @see #getCharset() * @since 1.257 */ protected String charset; - /** * Keeps this log entries. */ private boolean keepLog; - /** * If the build is in progress, remember {@link Runner} that's running it. * This field is not persisted. */ private volatile transient Runner runner; - protected static final ThreadLocal<SimpleDateFormat> ID_FORMATTER = new ThreadLocal<SimpleDateFormat>() { @Override @@ -238,11 +226,10 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run } /** - * Constructor for creating a {@link Run} object in - * an arbitrary state. + * Constructor for creating a {@link Run} object in an arbitrary state. */ protected Run(JobT job, Calendar timestamp) { - this(job,timestamp.getTimeInMillis()); + this(job, timestamp.getTimeInMillis()); } protected Run(JobT job, long timestamp) { @@ -263,28 +250,32 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run } /** - * Called after the build is loaded and the object is added to the build list. + * Called after the build is loaded and the object is added to the build + * list. */ protected void onLoad() { - for (Action a : getActions()) - if (a instanceof RunAction) + for (Action a : getActions()) { + if (a instanceof RunAction) { ((RunAction) a).onLoad(); + } + } } @Override public void addAction(Action a) { super.addAction(a); - if (a instanceof RunAction) + if (a instanceof RunAction) { ((RunAction) a).onAttached(this); + } } /*package*/ static long parseTimestampFromBuildDir(File buildDir) throws IOException { try { return ID_FORMATTER.get().parse(buildDir.getName()).getTime(); } catch (ParseException e) { - throw new IOException2("Invalid directory name "+buildDir,e); + throw new IOException2("Invalid directory name " + buildDir, e); } catch (NumberFormatException e) { - throw new IOException2("Invalid directory name "+buildDir,e); + throw new IOException2("Invalid directory name " + buildDir, e); } } @@ -293,7 +284,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run */ @SuppressWarnings({"unchecked"}) private RunT _this() { - return (RunT)this; + return (RunT) this; } /** @@ -306,8 +297,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run /** * Returns the build result. * - * <p> - * When a build is {@link #isBuilding() in progress}, this method + * <p> When a build is {@link #isBuilding() in progress}, this method * returns an intermediate result. */ @Exported @@ -317,44 +307,50 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run public void setResult(Result r) { // state can change only when we are building - assert state==State.BUILDING; + assert state == State.BUILDING; // result can only get worse - if(result==null) { + if (result == null) { result = r; - LOGGER.log(FINE, toString()+" : result is set to "+r,new Exception()); + LOGGER.log(FINE, toString() + " : result is set to " + r, new Exception()); } else { - if(r.isWorseThan(result)) { - LOGGER.log(FINE, toString()+" : result is set to "+r,new Exception()); + if (r.isWorseThan(result)) { + LOGGER.log(FINE, toString() + " : result is set to " + r, new Exception()); result = r; } } } /** - * Gets the subset of {@link #getActions()} that consists of {@link BuildBadgeAction}s. + * Gets the subset of {@link #getActions()} that consists of + * {@link BuildBadgeAction}s. */ public List<BuildBadgeAction> getBadgeActions() { List<BuildBadgeAction> r = null; for (Action a : getActions()) { - if(a instanceof BuildBadgeAction) { - if(r==null) + if (a instanceof BuildBadgeAction) { + if (r == null) { r = new ArrayList<BuildBadgeAction>(); - r.add((BuildBadgeAction)a); + } + r.add((BuildBadgeAction) a); } } - if(isKeepLog()) { - if(r==null) + if (isKeepLog()) { + if (r == null) { r = new ArrayList<BuildBadgeAction>(); + } r.add(new KeepLogBuildBadge()); } - if(r==null) return Collections.emptyList(); - else return r; + if (r == null) { + return Collections.emptyList(); + } else { + return r; + } } /** - * Returns true if the build is not completed yet. - * This includes "not started yet" state. + * Returns true if the build is not completed yet. This includes "not + * started yet" state. */ @Exported public boolean isBuilding() { @@ -373,10 +369,11 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run * Otherwise null. */ public Executor getExecutor() { - for( Computer c : Hudson.getInstance().getComputers() ) { + for (Computer c : Hudson.getInstance().getComputers()) { for (Executor e : c.getExecutors()) { - if(e.getCurrentExecutable()==this) + if (e.getCurrentExecutable() == this) { return e; + } } } return null; @@ -384,28 +381,32 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run /** * Gets the charset in which the log file is written. + * * @return never null. * @since 1.257 */ public final Charset getCharset() { - if(charset==null) return Charset.defaultCharset(); + if (charset == null) { + return Charset.defaultCharset(); + } return Charset.forName(charset); } /** * Returns the {@link Cause}s that tirggered a build. * - * <p> - * If a build sits in the queue for a long time, multiple build requests made during this period - * are all rolled up into one build, hence this method may return a list. + * <p> If a build sits in the queue for a long time, multiple build requests + * made during this period are all rolled up into one build, hence this + * method may return a list. * - * @return - * can be empty but never null. read-only. + * @return can be empty but never null. read-only. * @since 1.321 */ public List<Cause> getCauses() { CauseAction a = getAction(CauseAction.class); - if (a==null) return Collections.emptyList(); + if (a == null) { + return Collections.emptyList(); + } return Collections.unmodifiableList(a.getCauses()); } @@ -415,9 +416,11 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run * @since 1.362 */ public <T extends Cause> T getCause(Class<T> type) { - for (Cause c : getCauses()) - if (type.isInstance(c)) + for (Cause c : getCauses()) { + if (type.isInstance(c)) { return type.cast(c); + } + } return null; } @@ -428,16 +431,17 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run */ @Exported public final boolean isKeepLog() { - return getWhyKeepLog()!=null; + return getWhyKeepLog() != null; } /** - * If {@link #isKeepLog()} returns true, returns a human readable - * one-line string that explains why it's being kept. + * If {@link #isKeepLog()} returns true, returns a human readable one-line + * string that explains why it's being kept. */ public String getWhyKeepLog() { - if(keepLog) + if (keepLog) { return Messages.Run_MarkedExplicitly(); + } return null; // not marked at all } @@ -466,7 +470,8 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run } /** - * Same as {@link #getTimestamp()} but in a different type, that is since the time of the epoc. + * Same as {@link #getTimestamp()} but in a different type, that is since + * the time of the epoc. */ public final long getTimeInMillis() { return timestamp; @@ -477,9 +482,9 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run return description; } - /** * Returns the length-limited description. + * * @return The length-limited description. */ public String getTruncatedDescription() { @@ -495,9 +500,9 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run int displayChars = 0; int lastTruncatablePoint = -1; - for (int i=0; i<sz; i++) { + for (int i = 0; i < sz; i++) { char ch = description.charAt(i); - if(ch == '<') { + if (ch == '<') { inTag = true; } else if (ch == '>') { inTag = false; @@ -516,25 +521,25 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run String truncDesc = description; // Could not find a preferred truncable index, force a trunc at maxTruncLength - if (lastTruncatablePoint == -1) + if (lastTruncatablePoint == -1) { lastTruncatablePoint = maxTruncLength; + } if (displayChars >= maxDescrLength) { truncDesc = truncDesc.substring(0, lastTruncatablePoint) + ending; } - + return truncDesc; - + } /** * Gets the string that says how long since this build has started. * - * @return - * string like "3 minutes" "1 day" etc. + * @return string like "3 minutes" "1 day" etc. */ public String getTimestampString() { - long duration = new GregorianCalendar().getTimeInMillis()-timestamp; + long duration = new GregorianCalendar().getTimeInMillis() - timestamp; return Util.getPastTimeString(duration); } @@ -549,9 +554,10 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run * Gets the string that says how long the build took to run. */ public String getDurationString() { - if(isBuilding()) + if (isBuilding()) { return Messages.Run_InProgressDuration( - Util.getTimeSpanString(System.currentTimeMillis()-timestamp)); + Util.getTimeSpanString(System.currentTimeMillis() - timestamp)); + } return Util.getTimeSpanString(duration); } @@ -567,17 +573,18 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run * Gets the icon color for display. */ public BallColor getIconColor() { - if(!isBuilding()) { + if (!isBuilding()) { // already built return getResult().color; } // a new build is in progress BallColor baseColor; - if(previousBuild==null) + if (previousBuild == null) { baseColor = BallColor.GREY; - else + } else { baseColor = previousBuild.getIconColor(); + } return baseColor.anime(); } @@ -586,7 +593,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run * Returns true if the build is still queued and hasn't started yet. */ public boolean hasntStartedYet() { - return state ==State.NOT_STARTED; + return state == State.NOT_STARTED; } @Override @@ -595,25 +602,24 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run } public String getFullName() { - return project.getFullName()+" #"+number; + return project.getFullName() + " #" + number; } @Exported public String getFullDisplayName() { - return project.getFullDisplayName()+' '+getDisplayName(); + return project.getFullDisplayName() + ' ' + getDisplayName(); } public String getDisplayName() { - return displayName!=null ? displayName : "#"+number; + return displayName != null ? displayName : "#" + number; } public boolean hasCustomDisplayName() { - return displayName!=null; + return displayName != null; } /** - * @param value - * Set to null to revert back to the default "#NNN". + * @param value Set to null to revert back to the default "#NNN". */ public void setDisplayName(String value) throws IOException { checkPermission(UPDATE); @@ -621,7 +627,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run save(); } - @Exported(visibility=2) + @Exported(visibility = 2) public int getNumber() { return number; } @@ -631,36 +637,39 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run } /** - * Gets the most recent {@linkplain #isBuilding() completed} build excluding 'this' Run itself. + * Gets the most recent {@linkplain #isBuilding() completed} build excluding + * 'this' Run itself. */ public final RunT getPreviousCompletedBuild() { - RunT r=getPreviousBuild(); - while (r!=null && r.isBuilding()) - r=r.getPreviousBuild(); + RunT r = getPreviousBuild(); + while (r != null && r.isBuilding()) { + r = r.getPreviousBuild(); + } return r; } /** - * Obtains the next younger build in progress. It uses a skip-pointer so that we can compute this without - * O(n) computation time. This method also fixes up the skip list as we go, in a way that's concurrency safe. + * Obtains the next younger build in progress. It uses a skip-pointer so + * that we can compute this without O(n) computation time. This method also + * fixes up the skip list as we go, in a way that's concurrency safe. * - * <p> - * We basically follow the existing skip list, and wherever we find a non-optimal pointer, we remember them - * in 'fixUp' and update them later. + * <p> We basically follow the existing skip list, and wherever we find a + * non-optimal pointer, we remember them in 'fixUp' and update them later. */ public final RunT getPreviousBuildInProgress() { - if(previousBuildInProgress==this) return null; // the most common case - + if (previousBuildInProgress == this) { + return null; // the most common case + } List<RunT> fixUp = new ArrayList<RunT>(); RunT r = _this(); // 'r' is the source of the pointer (so that we can add it to fix up if we find that the target of the pointer is inefficient.) RunT answer; while (true) { RunT n = r.previousBuildInProgress; - if (n==null) {// no field computed yet. - n=r.getPreviousBuild(); + if (n == null) {// no field computed yet. + n = r.getPreviousBuild(); fixUp.add(r); } - if (r==n || n==null) { + if (r == n || n == null) { // this indicates that we know there's no build in progress beyond this point answer = null; break; @@ -676,19 +685,22 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run } // fix up so that the next look up will run faster - for (RunT f : fixUp) - f.previousBuildInProgress = answer==null ? f : answer; + for (RunT f : fixUp) { + f.previousBuildInProgress = answer == null ? f : answer; + } return answer; } /** - * Returns the last build that was actually built - i.e., skipping any with Result.NOT_BUILT + * Returns the last build that was actually built - i.e., skipping any with + * Result.NOT_BUILT */ public RunT getPreviousBuiltBuild() { - RunT r=previousBuild; + RunT r = previousBuild; // in certain situations (aborted m2 builds) r.getResult() can still be null, although it should theoretically never happen - while( r!=null && (r.getResult() == null || r.getResult()==Result.NOT_BUILT) ) - r=r.previousBuild; + while (r != null && (r.getResult() == null || r.getResult() == Result.NOT_BUILT)) { + r = r.previousBuild; + } return r; } @@ -696,9 +708,10 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run * Returns the last build that didn't fail before this build. */ public RunT getPreviousNotFailedBuild() { - RunT r=previousBuild; - while( r!=null && r.getResult()==Result.FAILURE ) - r=r.previousBuild; + RunT r = previousBuild; + while (r != null && r.getResult() == Result.FAILURE) { + r = r.previousBuild; + } return r; } @@ -706,45 +719,49 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run * Returns the last failed build before this build. */ public RunT getPreviousFailedBuild() { - RunT r=previousBuild; - while( r!=null && r.getResult()!=Result.FAILURE ) - r=r.previousBuild; + RunT r = previousBuild; + while (r != null && r.getResult() != Result.FAILURE) { + r = r.previousBuild; + } return r; } /** * Returns the last successful build before this build. + * * @since 1.383 */ public RunT getPreviousSuccessfulBuild() { - RunT r=previousBuild; - while( r!=null && r.getResult()!=Result.SUCCESS ) - r=r.previousBuild; + RunT r = previousBuild; + while (r != null && r.getResult() != Result.SUCCESS) { + r = r.previousBuild; + } return r; } /** - * Returns the last 'numberOfBuilds' builds with a build result >= 'threshold'. - * + * Returns the last 'numberOfBuilds' builds with a build result >= + * 'threshold'. + * * @param numberOfBuilds the desired number of builds * @param threshold the build result threshold - * @return a list with the builds (youngest build first). - * May be smaller than 'numberOfBuilds' or even empty - * if not enough builds satisfying the threshold have been found. Never null. + * @return a list with the builds (youngest build first). May be smaller + * than 'numberOfBuilds' or even empty if not enough builds satisfying the + * threshold have been found. Never null. * @since 1.383 */ public List<RunT> getPreviousBuildsOverThreshold(int numberOfBuilds, Result threshold) { List<RunT> builds = new ArrayList<RunT>(numberOfBuilds); - + RunT r = getPreviousBuild(); while (r != null && builds.size() < numberOfBuilds) { - if (!r.isBuilding() && - (r.getResult() != null && r.getResult().isBetterOrEqualTo(threshold))) { + if (!r.isBuilding() + && (r.getResult() != null && r.getResult().isBetterOrEqualTo(threshold))) { builds.add(r); } r = r.getPreviousBuild(); } - + return builds; } @@ -753,33 +770,34 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run } /** - * Returns the URL of this {@link Run}, relative to the context root of Hudson. + * Returns the URL of this {@link Run}, relative to the context root of + * Hudson. * - * @return - * String like "job/foo/32/" with trailing slash but no leading slash. + * @return String like "job/foo/32/" with trailing slash but no leading + * slash. */ // I really messed this up. I'm hoping to fix this some time // it shouldn't have trailing '/', and instead it should have leading '/' public String getUrl() { - return project.getUrl()+getNumber()+'/'; + return project.getUrl() + getNumber() + '/'; } /** * Obtains the absolute URL to this build. * - * @deprecated - * This method shall <b>NEVER</b> be used during HTML page rendering, as it won't work with - * network set up like Apache reverse proxy. - * This method is only intended for the remote API clients who cannot resolve relative references - * (even this won't work for the same reason, which should be fixed.) + * @deprecated This method shall <b>NEVER</b> be used during HTML page + * rendering, as it won't work with network set up like Apache reverse + * proxy. This method is only intended for the remote API clients who cannot + * resolve relative references (even this won't work for the same reason, + * which should be fixed.) */ - @Exported(visibility=2,name="url") + @Exported(visibility = 2, name = "url") public final String getAbsoluteUrl() { - return project.getAbsoluteUrl()+getNumber()+'/'; + return project.getAbsoluteUrl() + getNumber() + '/'; } public final String getSearchUrl() { - return getNumber()+"/"; + return getNumber() + "/"; } /** @@ -789,14 +807,14 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run public String getId() { return ID_FORMATTER.get().format(new Date(timestamp)); } - + /** - * Get the date formatter used to convert the directory name in to a timestamp - * This is nasty exposure of private data, but needed all the time the directory - * containing the build is used as it's timestamp. + * Get the date formatter used to convert the directory name in to a + * timestamp This is nasty exposure of private data, but needed all the time + * the directory containing the build is used as it's timestamp. */ public static DateFormat getIDFormatter() { - return ID_FORMATTER.get(); + return ID_FORMATTER.get(); } public Descriptor getDescriptorByName(String className) { @@ -809,7 +827,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run * Files related to this {@link Run} should be stored below this directory. */ public File getRootDir() { - File f = new File(project.getBuildDir(),getId()); + File f = new File(project.getBuildDir(), getId()); f.mkdirs(); return f; } @@ -818,7 +836,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run * Gets the directory where the artifacts are archived. */ public File getArtifactsDir() { - return new File(getRootDir(),"archive"); + return new File(getRootDir(), "archive"); } /** @@ -834,7 +852,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run */ public List<Artifact> getArtifactsUpTo(int n) { ArtifactList r = new ArtifactList(); - addArtifacts(getArtifactsDir(),"","",r,null,n); + addArtifacts(getArtifactsDir(), "", "", r, null, n); r.computeDisplayName(); return r; } @@ -842,16 +860,17 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run /** * Returns true if this run has any artifacts. * - * <p> - * The strange method name is so that we can access it from EL. + * <p> The strange method name is so that we can access it from EL. */ public boolean getHasArtifacts() { return !getArtifactsUpTo(1).isEmpty(); } - private int addArtifacts( File dir, String path, String pathHref, ArtifactList r, Artifact parent, int upTo ) { + private int addArtifacts(File dir, String path, String pathHref, ArtifactList r, Artifact parent, int upTo) { String[] children = dir.list(); - if(children==null) return 0; + if (children == null) { + return 0; + } Arrays.sort(children, String.CASE_INSENSITIVE_ORDER); int n = 0; @@ -859,88 +878,96 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run String childPath = path + child; String childHref = pathHref + Util.rawEncode(child); File sub = new File(dir, child); - boolean collapsed = (children.length==1 && parent!=null); + boolean collapsed = (children.length == 1 && parent != null); Artifact a; if (collapsed) { // Collapse single items into parent node where possible: a = new Artifact(parent.getFileName() + '/' + child, childPath, - sub.isDirectory() ? null : childHref, parent.getTreeNodeId()); + sub.isDirectory() ? null : childHref, parent.getTreeNodeId()); r.tree.put(a, r.tree.remove(parent)); } else { // Use null href for a directory: a = new Artifact(child, childPath, - sub.isDirectory() ? null : childHref, "n" + ++r.idSeq); - r.tree.put(a, parent!=null ? parent.getTreeNodeId() : null); + sub.isDirectory() ? null : childHref, "n" + ++r.idSeq); + r.tree.put(a, parent != null ? parent.getTreeNodeId() : null); } if (sub.isDirectory()) { - n += addArtifacts(sub, childPath + '/', childHref + '/', r, a, upTo-n); - if (n>=upTo) break; + n += addArtifacts(sub, childPath + '/', childHref + '/', r, a, upTo - n); + if (n >= upTo) { + break; + } } else { // Don't store collapsed path in ArrayList (for correct data in external API) r.add(collapsed ? new Artifact(child, a.relativePath, a.href, a.treeNodeId) : a); - if (++n>=upTo) break; + if (++n >= upTo) { + break; + } } } return n; } - /** - * Maximum number of artifacts to list before using switching to the tree view. + * Maximum number of artifacts to list before using switching to the tree + * view. */ public static final int LIST_CUTOFF = Integer.parseInt(System.getProperty("hudson.model.Run.ArtifactList.listCutoff", "16")); - /** - * Maximum number of artifacts to show in tree view before just showing a link. + * Maximum number of artifacts to show in tree view before just showing a + * link. */ public static final int TREE_CUTOFF = Integer.parseInt(System.getProperty("hudson.model.Run.ArtifactList.treeCutoff", "40")); // ..and then "too many" - public final class ArtifactList extends ArrayList<Artifact> { + /** - * Map of Artifact to treeNodeId of parent node in tree view. - * Contains Artifact objects for directories and files (the ArrayList contains only files). + * Map of Artifact to treeNodeId of parent node in tree view. Contains + * Artifact objects for directories and files (the ArrayList contains + * only files). */ - private LinkedHashMap<Artifact,String> tree = new LinkedHashMap<Artifact,String>(); + private LinkedHashMap<Artifact, String> tree = new LinkedHashMap<Artifact, String>(); private int idSeq = 0; - public Map<Artifact,String> getTree() { + public Map<Artifact, String> getTree() { return tree; } public void computeDisplayName() { - if(size()>LIST_CUTOFF) return; // we are not going to display file names, so no point in computing this - + if (size() > LIST_CUTOFF) { + return; // we are not going to display file names, so no point in computing this + } int maxDepth = 0; int[] len = new int[size()]; String[][] tokens = new String[size()][]; - for( int i=0; i<tokens.length; i++ ) { + for (int i = 0; i < tokens.length; i++) { tokens[i] = get(i).relativePath.split("[\\\\/]+"); - maxDepth = Math.max(maxDepth,tokens[i].length); + maxDepth = Math.max(maxDepth, tokens[i].length); len[i] = 1; } boolean collision; - int depth=0; + int depth = 0; do { collision = false; - Map<String,Integer/*index*/> names = new HashMap<String,Integer>(); + Map<String, Integer/*index*/> names = new HashMap<String, Integer>(); for (int i = 0; i < tokens.length; i++) { String[] token = tokens[i]; - String displayName = combineLast(token,len[i]); + String displayName = combineLast(token, len[i]); Integer j = names.put(displayName, i); - if(j!=null) { + if (j != null) { collision = true; - if(j>=0) + if (j >= 0) { len[j]++; + } len[i]++; - names.put(displayName,-1); // occupy this name but don't let len[i] incremented with additional collisions + names.put(displayName, -1); // occupy this name but don't let len[i] incremented with additional collisions } } - } while(collision && depth++<maxDepth); + } while (collision && depth++ < maxDepth); - for (int i = 0; i < tokens.length; i++) - get(i).displayPath = combineLast(tokens[i],len[i]); + for (int i = 0; i < tokens.length; i++) { + get(i).displayPath = combineLast(tokens[i], len[i]); + } // OUTER: // for( int n=1; n<maxLen; n++ ) { @@ -970,8 +997,10 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run */ private String combineLast(String[] token, int n) { StringBuilder buf = new StringBuilder(); - for( int i=Math.max(0,token.length-n); i<token.length; i++ ) { - if(buf.length()>0) buf.append('/'); + for (int i = Math.max(0, token.length - n); i < token.length; i++) { + if (buf.length() > 0) { + buf.append('/'); + } buf.append(token[i]); } return buf.toString(); @@ -983,30 +1012,28 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run */ @ExportedBean public class Artifact { + /** * Relative path name from {@link Run#getArtifactsDir()} */ - @Exported(visibility=3) + @Exported(visibility = 3) public final String relativePath; - /** - * Truncated form of {@link #relativePath} just enough - * to disambiguate {@link Artifact}s. + * Truncated form of {@link #relativePath} just enough to disambiguate + * {@link Artifact}s. */ /*package*/ String displayPath; - /** - * The filename of the artifact. - * (though when directories with single items are collapsed for tree view, name may - * include multiple path components, like "dist/pkg/mypkg") + * The filename of the artifact. (though when directories with single + * items are collapsed for tree view, name may include multiple path + * components, like "dist/pkg/mypkg") */ private String name; - /** - * Properly encoded relativePath for use in URLs. This field is null for directories. + * Properly encoded relativePath for use in URLs. This field is null for + * directories. */ private String href; - /** * Id of this node for use in tree view. */ @@ -1023,18 +1050,18 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run * Gets the artifact file. */ public File getFile() { - return new File(getArtifactsDir(),relativePath); + return new File(getArtifactsDir(), relativePath); } /** * Returns just the file name portion, without the path. */ - @Exported(visibility=3) + @Exported(visibility = 3) public String getFileName() { return name; } - @Exported(visibility=3) + @Exported(visibility = 3) public String getDisplayPath() { return displayPath; } @@ -1057,62 +1084,66 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run * Returns the log file. */ public File getLogFile() { - return new File(getRootDir(),"log"); + return new File(getRootDir(), "log"); } /** - * Returns an input stream that reads from the log file. - * It will use a gzip-compressed log file (log.gz) if that exists. + * Returns an input stream that reads from the log file. It will use a + * gzip-compressed log file (log.gz) if that exists. * - * @throws IOException + * @throws IOException * @return an input stream from the log file, or null if none exists * @since 1.349 */ public InputStream getLogInputStream() throws IOException { - File logFile = getLogFile(); - if (logFile.exists() ) { + File logFile = getLogFile(); + if (logFile.exists()) { return new FileInputStream(logFile); - } + } - File compressedLogFile = new File(logFile.getParentFile(), logFile.getName()+ ".gz"); - if (compressedLogFile.exists()) { + File compressedLogFile = new File(logFile.getParentFile(), logFile.getName() + ".gz"); + if (compressedLogFile.exists()) { return new GZIPInputStream(new FileInputStream(compressedLogFile)); - } - - return new NullInputStream(0); + } + + return new NullInputStream(0); } public Reader getLogReader() throws IOException { - if (charset==null) return new InputStreamReader(getLogInputStream()); - else return new InputStreamReader(getLogInputStream(),charset); + if (charset == null) { + return new InputStreamReader(getLogInputStream()); + } else { + return new InputStreamReader(getLogInputStream(), charset); + } } /** - * Used from <tt>console.jelly</tt> to write annotated log to the given output. + * Used from <tt>console.jelly</tt> to write annotated log to the given + * output. * * @since 1.349 */ public void writeLogTo(long offset, XMLOutput out) throws IOException { try { - getLogText().writeHtmlTo(offset,out.asWriter()); - } catch (IOException e) { - // try to fall back to the old getLogInputStream() - // mainly to support .gz compressed files - // In this case, console annotation handling will be turned off. - InputStream input = getLogInputStream(); - try { - IOUtils.copy(input, out.asWriter()); - } finally { - IOUtils.closeQuietly(input); - } - } + getLogText().writeHtmlTo(offset, out.asWriter()); + } catch (IOException e) { + // try to fall back to the old getLogInputStream() + // mainly to support .gz compressed files + // In this case, console annotation handling will be turned off. + InputStream input = getLogInputStream(); + try { + IOUtils.copy(input, out.asWriter()); + } finally { + IOUtils.closeQuietly(input); + } + } } /** * Used to URL-bind {@link AnnotatedLargeText}. */ public AnnotatedLargeText getLogText() { - return new AnnotatedLargeText(getLogFile(),getCharset(),!isLogUpdated(),this); + return new AnnotatedLargeText(getLogFile(), getCharset(), !isLogUpdated(), this); } @Override @@ -1121,8 +1152,9 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run .add("console") .add("changes"); for (Action a : getActions()) { - if(a.getIconFileName()!=null) + if (a.getIconFileName() != null) { builder.add(a.getUrlName()); + } } return builder; } @@ -1145,10 +1177,9 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run } /** - * Deletes this build's artifacts. + * Deletes this build's artifacts. * - * @throws IOException - * if we fail to delete. + * @throws IOException if we fail to delete. * * @since 1.350 */ @@ -1161,8 +1192,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run /** * Deletes this build and its entire log * - * @throws IOException - * if we fail to delete. + * @throws IOException if we fail to delete. */ public synchronized void delete() throws IOException { RunListener.fireDeleted(this); @@ -1172,27 +1202,28 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run link.delete(); File rootDir = getRootDir(); - File tmp = new File(rootDir.getParentFile(),'.'+rootDir.getName()); - + File tmp = new File(rootDir.getParentFile(), '.' + rootDir.getName()); + boolean renamingSucceeded = rootDir.renameTo(tmp); Util.deleteRecursive(tmp); // some user reported that they see some left-over .xyz files in the workspace, // so just to make sure we've really deleted it, schedule the deletion on VM exit, too. - if(tmp.exists()) + if (tmp.exists()) { tmp.deleteOnExit(); + } - if(!renamingSucceeded) - throw new IOException(rootDir+" is in use"); + if (!renamingSucceeded) { + throw new IOException(rootDir + " is in use"); + } removeRunFromParent(); } @SuppressWarnings("unchecked") // seems this is too clever for Java's type system? private void removeRunFromParent() { - getParent().removeRun((RunT)this); + getParent().removeRun((RunT) this); } - /** * @see CheckPoint#report() */ @@ -1204,34 +1235,38 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run * @see CheckPoint#block() */ /*package*/ static void waitForCheckpoint(CheckPoint id) throws InterruptedException { - while(true) { + while (true) { Run b = RunnerStack.INSTANCE.peek().getBuild().getPreviousBuildInProgress(); - if(b==null) return; // no pending earlier build + if (b == null) { + return; // no pending earlier build + } Run.Runner runner = b.runner; - if(runner==null) { + if (runner == null) { // polled at the wrong moment. try again. Thread.sleep(0); continue; } - if(runner.checkpoints.waitForCheckPoint(id)) + if (runner.checkpoints.waitForCheckPoint(id)) { return; // confirmed that the previous build reached the check point - + } // the previous build finished without ever reaching the check point. try again. } } protected abstract class Runner { + /** - * Keeps track of the check points attained by a build, and abstracts away the synchronization needed to - * maintain this data structure. + * Keeps track of the check points attained by a build, and abstracts + * away the synchronization needed to maintain this data structure. */ private final class CheckpointSet { + /** - * Stages of the builds that this runner has completed. This is used for concurrent {@link Runner}s to - * coordinate and serialize their executions where necessary. + * Stages of the builds that this runner has completed. This is used + * for concurrent {@link Runner}s to coordinate and serialize their + * executions where necessary. */ private final Set<CheckPoint> checkpoints = new HashSet<CheckPoint>(); - private boolean allDone; protected synchronized void report(CheckPoint identifier) { @@ -1242,10 +1277,11 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run protected synchronized boolean waitForCheckPoint(CheckPoint identifier) throws InterruptedException { final Thread t = Thread.currentThread(); final String oldName = t.getName(); - t.setName(oldName+" : waiting for "+identifier+" on "+getFullDisplayName()); + t.setName(oldName + " : waiting for " + identifier + " on " + getFullDisplayName()); try { - while(!allDone && !checkpoints.contains(identifier)) + while (!allDone && !checkpoints.contains(identifier)) { wait(); + } return checkpoints.contains(identifier); } finally { t.setName(oldName); @@ -1253,45 +1289,41 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run } /** - * Notifies that the build is fully completed and all the checkpoint locks be released. + * Notifies that the build is fully completed and all the checkpoint + * locks be released. */ private synchronized void allDone() { allDone = true; notifyAll(); } } - private final CheckpointSet checkpoints = new CheckpointSet(); /** * Performs the main build and returns the status code. * - * @throws Exception - * exception will be recorded and the build will be considered a failure. + * @throws Exception exception will be recorded and the build will be + * considered a failure. */ - public abstract Result run( BuildListener listener ) throws Exception, RunnerAbortedException; + public abstract Result run(BuildListener listener) throws Exception, RunnerAbortedException; /** - * Performs the post-build action. - * <p> - * This method is called after {@linkplain #run(BuildListener) the main portion of the build is completed.} - * This is a good opportunity to do notifications based on the result - * of the build. When this method is called, the build is not really - * finalized yet, and the build is still considered in progress --- for example, - * even if the build is successful, this build still won't be picked up - * by {@link Job#getLastSuccessfulBuild()}. + * Performs the post-build action. <p> This method is called after + * {@linkplain #run(BuildListener) the main portion of the build is completed.} + * This is a good opportunity to do notifications based on the result of + * the build. When this method is called, the build is not really + * finalized yet, and the build is still considered in progress --- for + * example, even if the build is successful, this build still won't be + * picked up by {@link Job#getLastSuccessfulBuild()}. */ - public abstract void post( BuildListener listener ) throws Exception; + public abstract void post(BuildListener listener) throws Exception; /** - * Performs final clean up action. - * <p> - * This method is called after {@link #post(BuildListener)}, - * after the build result is fully finalized. This is the point - * where the build is already considered completed. - * <p> - * Among other things, this is often a necessary pre-condition - * before invoking other builds that depend on this build. + * Performs final clean up action. <p> This method is called after + * {@link #post(BuildListener)}, after the build result is fully + * finalized. This is the point where the build is already considered + * completed. <p> Among other things, this is often a necessary + * pre-condition before invoking other builds that depend on this build. */ public abstract void cleanUp(BuildListener listener) throws Exception; @@ -1301,17 +1333,18 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run } /** - * Used in {@link Runner#run} to indicates that a fatal error in a build - * is reported to {@link BuildListener} and the build should be simply aborted + * Used in {@link Runner#run} to indicates that a fatal error in a build is + * reported to {@link BuildListener} and the build should be simply aborted * without further recording a stack trace. */ - public static final class RunnerAbortedException extends RuntimeException {} + public static final class RunnerAbortedException extends RuntimeException { + } protected final void run(Runner job) { - if(result!=null) + if (result != null) { return; // already built. - - StreamBuildListener listener=null; + } + StreamBuildListener listener = null; runner = job; onStartBuilding(); @@ -1345,35 +1378,35 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run } } - listener = new StreamBuildListener(logger,charset); + listener = new StreamBuildListener(logger, charset); listener.started(getCauses()); - RunListener.fireStarted(this,listener); + RunListener.fireStarted(this, listener); // create a symlink from build number to ID. - Util.createSymlink(getParent().getBuildDir(),getId(),String.valueOf(getNumber()),listener); + Util.createSymlink(getParent().getBuildDir(), getId(), String.valueOf(getNumber()), listener); setResult(job.run(listener)); - LOGGER.info(toString()+" main build action completed: "+result); + LOGGER.info(toString() + " main build action completed: " + result); CheckPoint.MAIN_COMPLETED.report(); } catch (ThreadDeath t) { throw t; - } catch( AbortException e ) {// orderly abortion. + } catch (AbortException e) {// orderly abortion. result = Result.FAILURE; listener.error(e.getMessage()); - LOGGER.log(FINE, "Build "+this+" aborted",e); - } catch( RunnerAbortedException e ) {// orderly abortion. + LOGGER.log(FINE, "Build " + this + " aborted", e); + } catch (RunnerAbortedException e) {// orderly abortion. result = Result.FAILURE; - LOGGER.log(FINE, "Build "+this+" aborted",e); - } catch( InterruptedException e) { + LOGGER.log(FINE, "Build " + this + " aborted", e); + } catch (InterruptedException e) { // aborted result = Result.ABORTED; listener.getLogger().println(Messages.Run_BuildAborted()); - LOGGER.log(Level.INFO,toString()+" aborted",e); - } catch( Throwable e ) { - handleFatalBuildProblem(listener,e); + LOGGER.log(Level.INFO, toString() + " aborted", e); + } catch (Throwable e) { + handleFatalBuildProblem(listener, e); result = Result.FAILURE; } @@ -1382,8 +1415,8 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run } catch (ThreadDeath t) { throw t; - } catch( Throwable e ) { - handleFatalBuildProblem(listener,e); + } catch (Throwable e) { + handleFatalBuildProblem(listener, e); result = Result.FAILURE; } finally { long end = System.currentTimeMillis(); @@ -1399,17 +1432,19 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run try { job.cleanUp(listener); } catch (Exception e) { - handleFatalBuildProblem(listener,e); + handleFatalBuildProblem(listener, e); // too late to update the result now } - RunListener.fireCompleted(this,listener); + RunListener.fireCompleted(this, listener); - if(listener!=null) + if (listener != null) { listener.finished(result); - if(listener!=null) + } + if (listener != null) { listener.closeQuietly(); - + } + if (logger != null) { IOUtils.closeQuietly(logger); } @@ -1417,16 +1452,16 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run try { save(); } catch (IOException e) { - LOGGER.log(Level.SEVERE, "Failed to save build record",e); + LOGGER.log(Level.SEVERE, "Failed to save build record", e); } } try { getParent().logRotate(); } catch (IOException e) { - LOGGER.log(Level.SEVERE, "Failed to rotate log",e); + LOGGER.log(Level.SEVERE, "Failed to rotate log", e); } catch (InterruptedException e) { - LOGGER.log(Level.SEVERE, "Failed to rotate log",e); + LOGGER.log(Level.SEVERE, "Failed to rotate log", e); } } finally { onEndBuilding(); @@ -1437,12 +1472,13 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run * Handles a fatal build problem (exception) that occurred during the build. */ private void handleFatalBuildProblem(BuildListener listener, Throwable e) { - if(listener!=null) { - if(e instanceof IOException) - Util.displayIOException((IOException)e,listener); + if (listener != null) { + if (e instanceof IOException) { + Util.displayIOException((IOException) e, listener); + } Writer w = listener.fatalError(e.getMessage()); - if(w!=null) { + if (w != null) { try { e.printStackTrace(new PrintWriter(w)); w.close(); @@ -1458,8 +1494,9 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run */ protected void onStartBuilding() { state = State.BUILDING; - if (runner!=null) + if (runner != null) { RunnerStack.INSTANCE.push(runner); + } } /** @@ -1467,7 +1504,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run */ protected void onEndBuilding() { // signal that we've finished building. - if (runner!=null) { + if (runner != null) { // MavenBuilds may be created without their corresponding runners. state = State.COMPLETED; runner.checkpoints.allDone(); @@ -1488,40 +1525,41 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run * Save the settings to a file. */ public synchronized void save() throws IOException { - if(BulkChange.contains(this)) return; + if (BulkChange.contains(this)) { + return; + } getDataFile().write(this); SaveableListener.fireOnChange(this, getDataFile()); } private XmlFile getDataFile() { - return new XmlFile(XSTREAM,new File(getRootDir(),"build.xml")); + return new XmlFile(XSTREAM, new File(getRootDir(), "build.xml")); } /** * Gets the log of the build as a string. * - * @deprecated since 2007-11-11. - * Use {@link #getLog(int)} instead as it avoids loading - * the whole log into memory unnecessarily. + * @deprecated since 2007-11-11. Use {@link #getLog(int)} instead as it + * avoids loading the whole log into memory unnecessarily. */ @Deprecated public String getLog() throws IOException { - return Util.loadFile(getLogFile(),getCharset()); + return Util.loadFile(getLogFile(), getCharset()); } /** - * Gets the log of the build as a list of strings (one per log line). - * The number of lines returned is constrained by the maxLines parameter. + * Gets the log of the build as a list of strings (one per log line). The + * number of lines returned is constrained by the maxLines parameter. * - * @param maxLines The maximum number of log lines to return. If the log - * is bigger than this, only the most recent lines are returned. - * @return A list of log lines. Will have no more than maxLines elements. + * @param maxLines The maximum number of log lines to return. If the log is + * bigger than this, only the most recent lines are returned. + * @return A list of log lines. Will have no more than maxLines elements. * @throws IOException If there is a problem reading the log file. */ public List<String> getLog(int maxLines) throws IOException { int lineCount = 0; List<String> logLines = new LinkedList<String>(); - BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(getLogFile()),getCharset())); + BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(getLogFile()), getCharset())); try { for (String line = reader.readLine(); line != null; line = reader.readLine()) { logLines.add(line); @@ -1530,8 +1568,9 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run // never have to hold the full contents of a huge log file in memory. // Adding to and removing from the ends of a linked list are cheap // operations. - if (lineCount > maxLines) + if (lineCount > maxLines) { logLines.remove(0); + } } } finally { reader.close(); @@ -1540,14 +1579,15 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run // If the log has been truncated, include that information. // Use set (replaces the first element) rather than add so that // the list doesn't grow beyond the specified maximum number of lines. - if (lineCount > maxLines) + if (lineCount > maxLines) { logLines.set(0, "[...truncated " + (lineCount - (maxLines - 1)) + " lines...]"); + } return ConsoleNote.removeNotes(logLines); } - public void doBuildStatus( StaplerRequest req, StaplerResponse rsp ) throws IOException { - rsp.sendRedirect2(req.getContextPath()+"/images/48x48/"+getBuildStatusUrl()); + public void doBuildStatus(StaplerRequest req, StaplerResponse rsp) throws IOException { + rsp.sendRedirect2(req.getContextPath() + "/images/48x48/" + getBuildStatusUrl()); } public String getBuildStatusUrl() { @@ -1555,6 +1595,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run } public static class Summary { + /** * Is this build worse or better, compared to the previous build? */ @@ -1582,42 +1623,51 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run public Summary getBuildStatusSummary() { Run prev = getPreviousBuild(); - if(getResult()==Result.SUCCESS) { - if(prev==null || prev.getResult()== Result.SUCCESS) + if (getResult() == Result.SUCCESS) { + if (prev == null || prev.getResult() == Result.SUCCESS) { return new Summary(false, Messages.Run_Summary_Stable()); - else + } else { return new Summary(false, Messages.Run_Summary_BackToNormal()); + } } - if(getResult()==Result.FAILURE) { + if (getResult() == Result.FAILURE) { RunT since = getPreviousNotFailedBuild(); - if(since==null) + if (since == null) { return new Summary(false, Messages.Run_Summary_BrokenForALongTime()); - if(since==prev) + } + if (since == prev) { return new Summary(true, Messages.Run_Summary_BrokenSinceThisBuild()); + } RunT failedBuild = since.getNextBuild(); return new Summary(false, Messages.Run_Summary_BrokenSince(failedBuild.getDisplayName())); } - if(getResult()==Result.ABORTED) + if (getResult() == Result.ABORTED) { return new Summary(false, Messages.Run_Summary_Aborted()); + } - if(getResult()==Result.UNSTABLE) { - if(((Run)this) instanceof AbstractBuild) { - AbstractTestResultAction trN = ((AbstractBuild)(Run)this).getTestResultAction(); - AbstractTestResultAction trP = prev==null ? null : ((AbstractBuild) prev).getTestResultAction(); - if(trP==null) { - if(trN!=null && trN.getFailCount()>0) + if (getResult() == Result.UNSTABLE) { + if (((Run) this) instanceof AbstractBuild) { + AbstractTestResultAction trN = ((AbstractBuild) (Run) this).getTestResultAction(); + AbstractTestResultAction trP = prev == null ? null : ((AbstractBuild) prev).getTestResultAction(); + if (trP == null) { + if (trN != null && trN.getFailCount() > 0) { return new Summary(false, Messages.Run_Summary_TestFailures(trN.getFailCount())); - else // ??? + } else // ??? + { return new Summary(false, Messages.Run_Summary_Unstable()); + } } - if(trP.getFailCount()==0) + if (trP.getFailCount() == 0) { return new Summary(true, Messages.Run_Summary_TestsStartedToFail(trN.getFailCount())); - if(trP.getFailCount() < trN.getFailCount()) - return new Summary(true, Messages.Run_Summary_MoreTestsFailing(trN.getFailCount()-trP.getFailCount(), trN.getFailCount())); - if(trP.getFailCount() > trN.getFailCount()) - return new Summary(false, Messages.Run_Summary_LessTestsFailing(trP.getFailCount()-trN.getFailCount(), trN.getFailCount())); + } + if (trP.getFailCount() < trN.getFailCount()) { + return new Summary(true, Messages.Run_Summary_MoreTestsFailing(trN.getFailCount() - trP.getFailCount(), trN.getFailCount())); + } + if (trP.getFailCount() > trN.getFailCount()) { + return new Summary(false, Messages.Run_Summary_LessTestsFailing(trP.getFailCount() - trN.getFailCount(), trN.getFailCount())); + } return new Summary(false, Messages.Run_Summary_TestsStillFailing(trN.getFailCount())); } @@ -1630,10 +1680,10 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run * Serves the artifacts. */ public DirectoryBrowserSupport doArtifact() { - if(Functions.isArtifactsPermissionEnabled()) { - checkPermission(ARTIFACTS); + if (Functions.isArtifactsPermissionEnabled()) { + checkPermission(ARTIFACTS); } - return new DirectoryBrowserSupport(this,new FilePath(getArtifactsDir()), project.getDisplayName()+' '+getDisplayName(), "package.png", true); + return new DirectoryBrowserSupport(this, new FilePath(getArtifactsDir()), project.getDisplayName() + ' ' + getDisplayName(), "package.png", true); } /** @@ -1649,13 +1699,13 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run /** * Returns the build time stamp in the body. */ - public void doBuildTimestamp( StaplerRequest req, StaplerResponse rsp, @QueryParameter String format) throws IOException { + public void doBuildTimestamp(StaplerRequest req, StaplerResponse rsp, @QueryParameter String format) throws IOException { rsp.setContentType("text/plain"); rsp.setCharacterEncoding("US-ASCII"); rsp.setStatus(HttpServletResponse.SC_OK); - DateFormat df = format==null ? - DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.SHORT, Locale.ENGLISH) : - new SimpleDateFormat(format,req.getLocale()); + DateFormat df = format == null + ? DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.ENGLISH) + : new SimpleDateFormat(format, req.getLocale()); rsp.getWriter().print(df.format(getTime())); } @@ -1668,7 +1718,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run FlushProofOutputStream out = null; try { out = new FlushProofOutputStream(rsp.getCompressedOutputStream(req)); - getLogText().writeLogTo(0,out); + getLogText().writeLogTo(0, out); } finally { IOUtils.closeQuietly(out); } @@ -1676,14 +1726,15 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run /** * Handles incremental log output. - * @deprecated as of 1.352 - * Use {@code getLogText().doProgressiveText(req,rsp)} + * + * @deprecated as of 1.352 Use + * {@code getLogText().doProgressiveText(req,rsp)} */ - public void doProgressiveLog( StaplerRequest req, StaplerResponse rsp) throws IOException { - getLogText().doProgressText(req,rsp); + public void doProgressiveLog(StaplerRequest req, StaplerResponse rsp) throws IOException { + getLogText().doProgressText(req, rsp); } - public void doToggleLogKeep( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { + public void doToggleLogKeep(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { keepLog(!keepLog); rsp.forwardToPreviousPage(req); } @@ -1691,7 +1742,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run /** * Marks this build to keep the log. */ - @CLIMethod(name="keep-build") + @CLIMethod(name = "keep-build") public final void keepLog() throws IOException { keepLog(true); } @@ -1705,7 +1756,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run /** * Deletes the build when the button is pressed. */ - public void doDoDelete( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { + public void doDoDelete(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { requirePOST(); checkPermission(DELETE); @@ -1713,13 +1764,13 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run // marked to be preserved, or if the build should not be deleted // due to dependencies! String why = getWhyKeepLog(); - if (why!=null) { - sendError(Messages.Run_UnableToDelete(toString(),why),req,rsp); + if (why != null) { + sendError(Messages.Run_UnableToDelete(toString(), why), req, rsp); return; } delete(); - rsp.sendRedirect2(req.getContextPath()+'/' + getParent().getUrl()); + rsp.sendRedirect2(req.getContextPath() + '/' + getParent().getUrl()); } public void setDescription(String description) throws IOException { @@ -1727,20 +1778,19 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run this.description = description; save(); } - + /** * Accepts the new description. */ - public synchronized void doSubmitDescription( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { + public synchronized void doSubmitDescription(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { setDescription(req.getParameter("description")); rsp.sendRedirect("."); // go to the top page } /** - * @deprecated as of 1.292 - * Use {@link #getEnvironment()} instead. + * @deprecated as of 1.292 Use {@link #getEnvironment()} instead. */ - public Map<String,String> getEnvVars() { + public Map<String, String> getEnvVars() { try { return getEnvironment(); } catch (IOException e) { @@ -1758,17 +1808,17 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run } /** - * Returns the map that contains environmental variables to be used for launching - * processes for this build. + * Returns the map that contains environmental variables to be used for + * launching processes for this build. + * + * <p> {@link BuildStep}s that invoke external processes should use this. + * This allows {@link BuildWrapper}s and other project configurations (such + * as JDK selection) to take effect. * - * <p> - * {@link BuildStep}s that invoke external processes should use this. - * This allows {@link BuildWrapper}s and other project configurations (such as JDK selection) - * to take effect. + * <p> Unlike earlier {@link #getEnvVars()}, this map contains the whole + * environment, not just the overrides, so one can introspect values to + * change its behavior. * - * <p> - * Unlike earlier {@link #getEnvVars()}, this map contains the whole environment, - * not just the overrides, so one can introspect values to change its behavior. * @since 1.305 */ public EnvVars getEnvironment(TaskListener log) throws IOException, InterruptedException { @@ -1813,16 +1863,17 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run } /** - * Builds up the environment variable map that's sufficient to identify a process - * as ours. This is used to kill run-away processes via {@link ProcessTree#killAll(Map)}. + * Builds up the environment variable map that's sufficient to identify a + * process as ours. This is used to kill run-away processes via + * {@link ProcessTree#killAll(Map)}. */ public final EnvVars getCharacteristicEnvVars() { EnvVars env = new EnvVars(); - env.put("HUDSON_SERVER_COOKIE",Util.getDigestOf("ServerID:"+Hudson.getInstance().getSecretKey())); - env.put("BUILD_NUMBER",String.valueOf(number)); - env.put("BUILD_ID",getId()); - env.put("BUILD_TAG","hudson-"+getParent().getName()+"-"+number); - env.put("JOB_NAME",getParent().getFullName()); + env.put("HUDSON_SERVER_COOKIE", Util.getDigestOf("ServerID:" + Hudson.getInstance().getSecretKey())); + env.put("BUILD_NUMBER", String.valueOf(number)); + env.put("BUILD_ID", getId()); + env.put("BUILD_TAG", "hudson-" + getParent().getName() + "-" + number); + env.put("JOB_NAME", getParent().getFullName()); return env; } @@ -1830,7 +1881,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run return project.getName() + "#" + getNumber(); } - public static Run<?,?> fromExternalizableId(String id) { + public static Run<?, ?> fromExternalizableId(String id) { int hash = id.lastIndexOf('#'); if (hash <= 0) { throw new IllegalArgumentException("Invalid id"); @@ -1838,15 +1889,16 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run String jobName = id.substring(0, hash); int number = Integer.parseInt(id.substring(hash + 1)); - Job<?,?> job = (Job<?,?>) Hudson.getInstance().getItem(jobName); + Job<?, ?> job = (Job<?, ?>) Hudson.getInstance().getItem(jobName); return job.getBuildByNumber(number); } /** * Returns the estimated duration for this run if it is currently running. - * Default to {@link Job#getEstimatedDuration()}, may be overridden in subclasses - * if duration may depend on run specific parameters (like incremental Maven builds). - * + * Default to {@link Job#getEstimatedDuration()}, may be overridden in + * subclasses if duration may depend on run specific parameters (like + * incremental Maven builds). + * * @return the estimated duration in milliseconds * @since 1.383 */ @@ -1854,7 +1906,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run return project.getEstimatedDuration(); } - public HttpResponse doConfigSubmit( StaplerRequest req ) throws IOException, ServletException, FormException { + public HttpResponse doConfigSubmit(StaplerRequest req) throws IOException, ServletException, FormException { checkPermission(UPDATE); BulkChange bc = new BulkChange(this); try { @@ -1871,47 +1923,47 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run setDisplayName(Util.fixEmptyAndTrim(json.getString("displayName"))); setDescription(json.getString("description")); } - public static final XStream XSTREAM = new XStream2(); + static { - XSTREAM.alias("build",FreeStyleBuild.class); - XSTREAM.alias("matrix-build",MatrixBuild.class); - XSTREAM.alias("matrix-run",MatrixRun.class); + XSTREAM.alias("build", FreeStyleBuild.class); + XSTREAM.alias("matrix-build", MatrixBuild.class); + XSTREAM.alias("matrix-run", MatrixRun.class); XSTREAM.registerConverter(Result.conv); } - private static final Logger LOGGER = Logger.getLogger(Run.class.getName()); - /** - * Sort by date. Newer ones first. + * Sort by date. Newer ones first. */ public static final Comparator<Run> ORDER_BY_DATE = new Comparator<Run>() { public int compare(Run lhs, Run rhs) { long lt = lhs.getTimeInMillis(); long rt = rhs.getTimeInMillis(); - if(lt>rt) return -1; - if(lt<rt) return 1; + if (lt > rt) { + return -1; + } + if (lt < rt) { + return 1; + } return 0; } }; - /** * {@link FeedAdapter} to produce feed from the summary of this build. */ public static final FeedAdapter<Run> FEED_ADAPTER = new DefaultFeedAdapter(); - /** * {@link FeedAdapter} to produce feeds to show one build per project. */ public static final FeedAdapter<Run> FEED_ADAPTER_LATEST = new DefaultFeedAdapter() { /** - * The entry unique ID needs to be tied to a project, so that - * new builds will replace the old result. + * The entry unique ID needs to be tied to a project, so that new builds + * will replace the old result. */ @Override public String getEntryID(Run e) { // can't use a meaningful year field unless we remember when the job was created. - return "tag:hudson.java.net,2008:"+e.getParent().getAbsoluteUrl(); + return "tag:hudson.java.net,2008:" + e.getParent().getAbsoluteUrl(); } }; @@ -1919,25 +1971,38 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run * {@link BuildBadgeAction} that shows the logs are being kept. */ public final class KeepLogBuildBadge implements BuildBadgeAction { - public String getIconFileName() { return null; } - public String getDisplayName() { return null; } - public String getUrlName() { return null; } - public String getWhyKeepLog() { return Run.this.getWhyKeepLog(); } - } - public static final PermissionGroup PERMISSIONS = new PermissionGroup(Run.class,Messages._Run_Permissions_Title()); - public static final Permission DELETE = new Permission(PERMISSIONS,"Delete",Messages._Run_DeletePermission_Description(),Permission.DELETE); - public static final Permission UPDATE = new Permission(PERMISSIONS,"Update",Messages._Run_UpdatePermission_Description(),Permission.UPDATE); - /** See {@link hudson.Functions#isArtifactsPermissionEnabled} */ - public static final Permission ARTIFACTS = new Permission(PERMISSIONS,"Artifacts",Messages._Run_ArtifactsPermission_Description(), null, - Functions.isArtifactsPermissionEnabled()); + public String getIconFileName() { + return null; + } + + public String getDisplayName() { + return null; + } + + public String getUrlName() { + return null; + } + + public String getWhyKeepLog() { + return Run.this.getWhyKeepLog(); + } + } + public static final PermissionGroup PERMISSIONS = new PermissionGroup(Run.class, Messages._Run_Permissions_Title()); + public static final Permission DELETE = new Permission(PERMISSIONS, "Delete", Messages._Run_DeletePermission_Description(), Permission.DELETE); + public static final Permission UPDATE = new Permission(PERMISSIONS, "Update", Messages._Run_UpdatePermission_Description(), Permission.UPDATE); + /** + * See {@link hudson.Functions#isArtifactsPermissionEnabled} + */ + public static final Permission ARTIFACTS = new Permission(PERMISSIONS, "Artifacts", Messages._Run_ArtifactsPermission_Description(), null, + Functions.isArtifactsPermissionEnabled()); private static class DefaultFeedAdapter implements FeedAdapter<Run> { private static final String DESCRIPTION_SUFIX = "description:"; public String getEntryTitle(Run entry) { - return entry+" ("+entry.getBuildStatusSummary().message+")"; + return entry + " (" + entry.getBuildStatusSummary().message + ")"; } public String getEntryUrl(Run entry) { @@ -1946,12 +2011,12 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run public String getEntryID(Run entry) { return "tag:" + "hudson.java.net," - + entry.getTimestamp().get(Calendar.YEAR) + ":" - + entry.getParent().getName()+':'+entry.getId(); + + entry.getTimestamp().get(Calendar.YEAR) + ":" + + entry.getParent().getName() + ':' + entry.getId(); } public String getEntryDescription(Run entry) { - return (entry.getDescription()!= null ? DESCRIPTION_SUFIX + entry.getDescription(): null); + return (entry.getDescription() != null ? DESCRIPTION_SUFIX + entry.getDescription() : null); } public Calendar getEntryTimestamp(Run entry) { @@ -1966,27 +2031,29 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run @Override public Object getDynamic(String token, StaplerRequest req, StaplerResponse rsp) { Object result = super.getDynamic(token, req, rsp); - if (result == null) - // Next/Previous Build links on an action page (like /job/Abc/123/testReport) - // will also point to same action (/job/Abc/124/testReport), but other builds - // may not have the action.. tell browsers to redirect up to the build page. + if (result == null) // Next/Previous Build links on an action page (like /job/Abc/123/testReport) + // will also point to same action (/job/Abc/124/testReport), but other builds + // may not have the action.. tell browsers to redirect up to the build page. + { result = new RedirectUp(); + } return result; } public static class RedirectUp { + public void doDynamic(StaplerResponse rsp) throws IOException { // Compromise to handle both browsers (auto-redirect) and programmatic access // (want accurate 404 response).. send 404 with javscript to redirect browsers. rsp.setStatus(HttpServletResponse.SC_NOT_FOUND); rsp.setContentType("text/html;charset=UTF-8"); PrintWriter out = rsp.getWriter(); - out.println("<html><head>" + - "<meta http-equiv='refresh' content='1;url=..'/>" + - "<script>window.location.replace('..');</script>" + - "</head>" + - "<body style='background-color:white; color:white;'>" + - "Not found</body></html>"); + out.println("<html><head>" + + "<meta http-equiv='refresh' content='1;url=..'/>" + + "<script>window.location.replace('..');</script>" + + "</head>" + + "<body style='background-color:white; color:white;'>" + + "Not found</body></html>"); out.flush(); } } |

