2006-11-06 05:16:01 +08:00
|
|
|
package hudson;
|
|
|
|
|
2007-04-11 13:11:12 +08:00
|
|
|
import hudson.maven.ExecutedMojo;
|
2007-10-29 07:20:01 +08:00
|
|
|
import hudson.model.AbstractProject;
|
2007-04-11 13:11:12 +08:00
|
|
|
import hudson.model.Action;
|
2007-10-29 07:20:01 +08:00
|
|
|
import hudson.model.Descriptor;
|
2006-12-30 03:15:53 +08:00
|
|
|
import hudson.model.Hudson;
|
2007-04-11 13:11:12 +08:00
|
|
|
import hudson.model.Item;
|
|
|
|
import hudson.model.ItemGroup;
|
|
|
|
import hudson.model.Items;
|
|
|
|
import hudson.model.Job;
|
|
|
|
import hudson.model.JobPropertyDescriptor;
|
2006-11-06 05:16:01 +08:00
|
|
|
import hudson.model.ModelObject;
|
|
|
|
import hudson.model.Node;
|
|
|
|
import hudson.model.Project;
|
|
|
|
import hudson.model.Run;
|
2007-04-11 13:11:12 +08:00
|
|
|
import hudson.model.TopLevelItem;
|
|
|
|
import hudson.model.View;
|
2007-08-05 03:33:27 +08:00
|
|
|
import hudson.search.SearchableModelObject;
|
2007-10-29 07:20:01 +08:00
|
|
|
import hudson.tasks.BuildStep;
|
|
|
|
import hudson.tasks.BuildStepDescriptor;
|
2007-10-29 07:39:31 +08:00
|
|
|
import hudson.tasks.BuildWrapper;
|
|
|
|
import hudson.tasks.BuildWrappers;
|
2007-10-29 07:20:01 +08:00
|
|
|
import hudson.tasks.Builder;
|
|
|
|
import hudson.tasks.Publisher;
|
2007-12-05 15:09:29 +08:00
|
|
|
import hudson.security.SecurityRealm;
|
2007-12-06 13:53:00 +08:00
|
|
|
import hudson.security.AuthorizationStrategy;
|
2007-12-18 14:55:31 +08:00
|
|
|
import hudson.security.Permission;
|
2007-04-11 13:11:12 +08:00
|
|
|
import org.apache.commons.jexl.parser.ASTSizeFunction;
|
2007-10-23 02:28:11 +08:00
|
|
|
import org.apache.commons.jexl.util.Introspector;
|
2007-12-25 01:06:32 +08:00
|
|
|
import org.apache.commons.jelly.JellyContext;
|
2006-11-06 05:16:01 +08:00
|
|
|
import org.kohsuke.stapler.Ancestor;
|
2007-04-11 13:11:12 +08:00
|
|
|
import org.kohsuke.stapler.Stapler;
|
2006-11-06 05:16:01 +08:00
|
|
|
import org.kohsuke.stapler.StaplerRequest;
|
2007-03-11 02:03:02 +08:00
|
|
|
import org.kohsuke.stapler.StaplerResponse;
|
2007-12-17 14:29:41 +08:00
|
|
|
import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
|
2006-11-06 05:16:01 +08:00
|
|
|
|
2007-04-11 13:11:12 +08:00
|
|
|
import javax.servlet.ServletException;
|
2006-11-06 05:16:01 +08:00
|
|
|
import javax.servlet.http.Cookie;
|
|
|
|
import javax.servlet.http.HttpServletRequest;
|
2006-12-30 03:15:53 +08:00
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
|
import java.io.File;
|
2007-01-20 15:31:24 +08:00
|
|
|
import java.io.IOException;
|
2007-09-23 00:39:31 +08:00
|
|
|
import java.io.PrintWriter;
|
2007-09-27 12:16:44 +08:00
|
|
|
import java.io.StringWriter;
|
2007-07-30 03:05:37 +08:00
|
|
|
import java.lang.management.LockInfo;
|
|
|
|
import java.lang.management.ManagementFactory;
|
|
|
|
import java.lang.management.MonitorInfo;
|
|
|
|
import java.lang.management.ThreadInfo;
|
|
|
|
import java.lang.management.ThreadMXBean;
|
2007-01-13 03:55:26 +08:00
|
|
|
import java.net.URI;
|
|
|
|
import java.net.URISyntaxException;
|
2006-12-30 03:15:53 +08:00
|
|
|
import java.util.Calendar;
|
2007-07-30 03:05:37 +08:00
|
|
|
import java.util.Collection;
|
|
|
|
import java.util.Collections;
|
2007-04-11 13:11:12 +08:00
|
|
|
import java.util.HashMap;
|
2006-11-06 05:16:01 +08:00
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.SortedMap;
|
2006-12-30 03:15:53 +08:00
|
|
|
import java.util.TreeMap;
|
2006-11-06 05:16:01 +08:00
|
|
|
import java.util.logging.LogRecord;
|
|
|
|
import java.util.logging.SimpleFormatter;
|
2007-09-27 12:16:44 +08:00
|
|
|
import java.util.regex.Pattern;
|
2006-11-06 05:16:01 +08:00
|
|
|
|
|
|
|
/**
|
2006-12-21 14:57:43 +08:00
|
|
|
* Utility functions used in views.
|
|
|
|
*
|
|
|
|
* <p>
|
2008-02-03 04:14:15 +08:00
|
|
|
* An instance of this class is created for each request and made accessible
|
|
|
|
* from view pages via the variable 'h' (h stands for Hudson.)
|
2006-12-21 14:57:43 +08:00
|
|
|
*
|
2006-11-06 05:16:01 +08:00
|
|
|
* @author Kohsuke Kawaguchi
|
|
|
|
*/
|
|
|
|
public class Functions {
|
2007-03-01 09:51:32 +08:00
|
|
|
private static volatile int globalIota = 0;
|
|
|
|
private int iota;
|
|
|
|
|
|
|
|
public 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;
|
|
|
|
}
|
2006-12-21 14:57:43 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates an unique ID.
|
|
|
|
*/
|
|
|
|
public String generateId() {
|
|
|
|
return "id"+iota++;
|
|
|
|
}
|
|
|
|
|
2006-11-06 05:16:01 +08:00
|
|
|
public static boolean isModel(Object o) {
|
|
|
|
return o instanceof ModelObject;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static String xsDate(Calendar cal) {
|
|
|
|
return Util.XS_DATETIME_FORMATTER.format(cal.getTime());
|
|
|
|
}
|
|
|
|
|
2006-11-25 04:08:18 +08:00
|
|
|
public static String rfc822Date(Calendar cal) {
|
|
|
|
return Util.RFC822_DATETIME_FORMATTER.format(cal.getTime());
|
|
|
|
}
|
|
|
|
|
2006-12-17 14:14:43 +08:00
|
|
|
/**
|
|
|
|
* Prints the integer as a string that represents difference,
|
|
|
|
* like "-5", "+/-0", "+3".
|
|
|
|
*/
|
2006-11-06 05:16:01 +08:00
|
|
|
public static String getDiffString(int i) {
|
|
|
|
if(i==0) return "\u00B10"; // +/-0
|
|
|
|
String s = Integer.toString(i);
|
|
|
|
if(i>0) return "+"+s;
|
|
|
|
else return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-04-07 02:10:17 +08:00
|
|
|
* {@link #getDiffString(int)} that doesn't show anything for +/-0
|
2006-11-06 05:16:01 +08:00
|
|
|
*/
|
|
|
|
public static String getDiffString2(int i) {
|
|
|
|
if(i==0) return "";
|
|
|
|
String s = Integer.toString(i);
|
|
|
|
if(i>0) return "+"+s;
|
|
|
|
else return s;
|
|
|
|
}
|
|
|
|
|
2008-04-07 02:10:17 +08:00
|
|
|
/**
|
|
|
|
* {@link #getDiffString2(int)} that puts the result into prefix and suffix
|
|
|
|
* if there's something to print
|
|
|
|
*/
|
|
|
|
public static String getDiffString2(String prefix, int i, String suffix) {
|
|
|
|
if(i==0) return "";
|
|
|
|
String s = Integer.toString(i);
|
|
|
|
if(i>0) return prefix+"+"+s+suffix;
|
|
|
|
else return prefix+s+suffix;
|
|
|
|
}
|
|
|
|
|
2006-11-06 05:16:01 +08:00
|
|
|
/**
|
|
|
|
* Adds the proper suffix.
|
|
|
|
*/
|
|
|
|
public static String addSuffix(int n, String singular, String plural) {
|
|
|
|
StringBuffer buf = new StringBuffer();
|
|
|
|
buf.append(n).append(' ');
|
|
|
|
if(n==1)
|
|
|
|
buf.append(singular);
|
|
|
|
else
|
|
|
|
buf.append(plural);
|
|
|
|
return buf.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
public static RunUrl decompose(StaplerRequest req) {
|
2006-11-23 08:30:05 +08:00
|
|
|
List<Ancestor> ancestors = req.getAncestors();
|
2007-06-25 06:37:24 +08:00
|
|
|
|
|
|
|
// find the first and last Run instances
|
|
|
|
Ancestor f=null,l=null;
|
2006-11-06 05:16:01 +08:00
|
|
|
for (Ancestor anc : ancestors) {
|
|
|
|
if(anc.getObject() instanceof Run) {
|
2007-06-25 06:37:24 +08:00
|
|
|
if(f==null) f=anc;
|
|
|
|
l=anc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(l==null) return null; // there was no Run object
|
|
|
|
|
|
|
|
String head = f.getPrev().getUrl()+'/';
|
|
|
|
String base = l.getUrl();
|
|
|
|
|
|
|
|
String reqUri = req.getOriginalRequestURI();
|
|
|
|
// despite the spec saying this string is not decoded,
|
|
|
|
// Tomcat apparently decodes this string. You see ' ' instead of '%20', which is what
|
|
|
|
// the browser has sent. So do some quick scan to see if it's ASCII safe, and if not
|
|
|
|
// re-encode it. Otherwise it won't match with ancUrl.
|
|
|
|
if(reqUri.indexOf(' ')>=0) {
|
|
|
|
try {
|
|
|
|
// 3 arg version accepts illegal character. 1-arg version doesn't
|
|
|
|
reqUri = new URI(null,reqUri,null).toASCIIString();
|
|
|
|
} catch (URISyntaxException e) {
|
|
|
|
// try to use reqUri as is.
|
2006-11-06 05:16:01 +08:00
|
|
|
}
|
|
|
|
}
|
2007-06-25 06:37:24 +08:00
|
|
|
|
|
|
|
String rest = reqUri.substring(f.getUrl().length());
|
|
|
|
|
|
|
|
return new RunUrl( (Run) f.getObject(), head, base, rest);
|
2006-11-06 05:16:01 +08:00
|
|
|
}
|
|
|
|
|
2007-06-25 06:37:24 +08:00
|
|
|
/**
|
|
|
|
* URL decomposed for easier computation of relevant URLs.
|
|
|
|
*
|
|
|
|
* <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 <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.
|
|
|
|
*/
|
2006-11-06 05:16:01 +08:00
|
|
|
public static final class RunUrl {
|
2007-06-25 06:37:24 +08:00
|
|
|
private final String head, base, rest;
|
2006-11-06 05:16:01 +08:00
|
|
|
private final Run run;
|
|
|
|
|
2007-06-25 06:37:24 +08:00
|
|
|
|
|
|
|
public RunUrl(Run run, String head, String base, String rest) {
|
2006-11-06 05:16:01 +08:00
|
|
|
this.run = run;
|
2007-06-25 06:37:24 +08:00
|
|
|
this.head = head;
|
|
|
|
this.base = base;
|
2006-11-06 05:16:01 +08:00
|
|
|
this.rest = rest;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getBaseUrl() {
|
2007-06-25 06:37:24 +08:00
|
|
|
return base;
|
2006-11-06 05:16:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the same page in the next build.
|
|
|
|
*/
|
|
|
|
public String getNextBuildUrl() {
|
|
|
|
return getUrl(run.getNextBuild());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the same page in the previous build.
|
|
|
|
*/
|
|
|
|
public String getPreviousBuildUrl() {
|
|
|
|
return getUrl(run.getPreviousBuild());
|
|
|
|
}
|
|
|
|
|
|
|
|
private String getUrl(Run n) {
|
|
|
|
if(n ==null)
|
|
|
|
return null;
|
|
|
|
else {
|
2007-06-25 06:37:24 +08:00
|
|
|
return head+n.getNumber()+rest;
|
2006-11-06 05:16:01 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Node.Mode[] getNodeModes() {
|
|
|
|
return Node.Mode.values();
|
2007-05-25 21:16:04 +08:00
|
|
|
}
|
|
|
|
|
2006-11-06 05:16:01 +08:00
|
|
|
public static String getProjectListString(List<Project> projects) {
|
2007-01-20 14:31:16 +08:00
|
|
|
return Items.toNameList(projects);
|
2006-11-06 05:16:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public static Object ifThenElse(boolean cond, Object thenValue, Object elseValue) {
|
|
|
|
return cond ? thenValue : elseValue;
|
|
|
|
}
|
2007-05-25 21:16:04 +08:00
|
|
|
|
2006-11-06 05:16:01 +08:00
|
|
|
public static String appendIfNotNull(String text, String suffix, String nullText) {
|
|
|
|
return text == null ? nullText : text + suffix;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Map getSystemProperties() {
|
2006-11-23 08:30:05 +08:00
|
|
|
return new TreeMap<Object,Object>(System.getProperties());
|
2006-11-06 05:16:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public static Map getEnvVars() {
|
2006-11-23 08:30:05 +08:00
|
|
|
return new TreeMap<String,String>(EnvVars.masterEnvVars);
|
2006-11-06 05:16:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public static boolean isWindows() {
|
|
|
|
return File.pathSeparatorChar==';';
|
|
|
|
}
|
|
|
|
|
|
|
|
public static List<LogRecord> getLogRecords() {
|
|
|
|
return Hudson.logRecords;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static String printLogRecord(LogRecord r) {
|
|
|
|
return formatter.format(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Cookie getCookie(HttpServletRequest req,String name) {
|
|
|
|
Cookie[] cookies = req.getCookies();
|
|
|
|
if(cookies!=null) {
|
|
|
|
for (Cookie cookie : cookies) {
|
|
|
|
if(cookie.getName().equals(name)) {
|
|
|
|
return cookie;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2006-11-17 00:29:56 +08:00
|
|
|
public static String getCookie(HttpServletRequest req,String name, String defaultValue) {
|
|
|
|
Cookie c = getCookie(req, name);
|
|
|
|
if(c==null || c.getValue()==null) return defaultValue;
|
|
|
|
return c.getValue();
|
|
|
|
}
|
|
|
|
|
2006-12-07 02:52:54 +08:00
|
|
|
/**
|
|
|
|
* Gets the suffix to use for YUI JavaScript.
|
|
|
|
*/
|
|
|
|
public static String getYuiSuffix() {
|
|
|
|
return DEBUG_YUI ? "debug" : "min";
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set to true if you need to use the debug version of YUI.
|
|
|
|
*/
|
2007-08-05 09:06:28 +08:00
|
|
|
public static boolean DEBUG_YUI = System.getProperty("debug.YUI")!=null;
|
2006-12-07 02:52:54 +08:00
|
|
|
|
2006-11-06 05:16:01 +08:00
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
return map.tailMap(Integer.parseInt(to));
|
|
|
|
|
|
|
|
return map.subMap(Integer.parseInt(to),Integer.parseInt(from)-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final SimpleFormatter formatter = new SimpleFormatter();
|
2006-12-03 07:53:00 +08:00
|
|
|
|
2006-12-15 14:04:38 +08:00
|
|
|
/**
|
|
|
|
* 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
|
2007-05-25 21:16:04 +08:00
|
|
|
* with auto refresh. On those pages, disable auto-refresh.
|
2006-12-15 14:04:38 +08:00
|
|
|
*/
|
|
|
|
public static void configureAutoRefresh(HttpServletRequest request, HttpServletResponse response, boolean noAutoRefresh) {
|
|
|
|
if(noAutoRefresh)
|
|
|
|
return;
|
|
|
|
|
2006-12-03 07:53:00 +08:00
|
|
|
String param = request.getParameter("auto_refresh");
|
|
|
|
boolean refresh = isAutoRefresh(request);
|
|
|
|
if (param != null) {
|
|
|
|
refresh = Boolean.parseBoolean(param);
|
|
|
|
Cookie c = new Cookie("hudson_auto_refresh", Boolean.toString(refresh));
|
|
|
|
// Need to set path or it will not stick from e.g. a project page to the dashboard.
|
|
|
|
// 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("/");
|
2007-07-11 22:06:14 +08:00
|
|
|
c.setMaxAge(60*60*24*30); // persist it roughly for a month
|
2006-12-03 07:53:00 +08:00
|
|
|
response.addCookie(c);
|
|
|
|
}
|
|
|
|
if (refresh) {
|
|
|
|
response.addHeader("Refresh", "10");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static boolean isAutoRefresh(HttpServletRequest request) {
|
|
|
|
String param = request.getParameter("auto_refresh");
|
|
|
|
if (param != null) {
|
|
|
|
return Boolean.parseBoolean(param);
|
|
|
|
}
|
2006-12-30 03:15:53 +08:00
|
|
|
Cookie[] cookies = request.getCookies();
|
|
|
|
if(cookies==null)
|
|
|
|
return false; // when API design messes it up, we all suffer
|
|
|
|
|
|
|
|
for (Cookie c : cookies) {
|
2006-12-03 07:53:00 +08:00
|
|
|
if (c.getName().equals("hudson_auto_refresh")) {
|
|
|
|
return Boolean.parseBoolean(c.getValue());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2007-01-08 05:00:49 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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) {
|
|
|
|
List list = req.getAncestors();
|
|
|
|
for( int i=list.size()-1; i>=0; i-- ) {
|
|
|
|
Ancestor anc = (Ancestor) list.get(i);
|
|
|
|
if(anc.getObject()==it)
|
|
|
|
return anc.getUrl();
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2007-01-18 23:07:20 +08:00
|
|
|
|
2007-08-05 03:33:27 +08:00
|
|
|
/**
|
|
|
|
* Finds the inner-most {@link SearchableModelObject} in scope.
|
|
|
|
*/
|
|
|
|
public static String getSearchURL() {
|
|
|
|
List list = Stapler.getCurrentRequest().getAncestors();
|
|
|
|
for( int i=list.size()-1; i>=0; i-- ) {
|
|
|
|
Ancestor anc = (Ancestor) list.get(i);
|
|
|
|
if(anc.getObject() instanceof SearchableModelObject)
|
2007-08-05 04:10:28 +08:00
|
|
|
return anc.getUrl()+"/search/";
|
2007-08-05 03:33:27 +08:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2007-01-18 23:07:20 +08:00
|
|
|
public static String appendSpaceIfNotNull(String n) {
|
|
|
|
if(n==null) return null;
|
|
|
|
else return n+' ';
|
|
|
|
}
|
2007-01-20 15:31:24 +08:00
|
|
|
|
|
|
|
public static String getWin32ErrorMessage(IOException e) {
|
|
|
|
return Util.getWin32ErrorMessage(e);
|
|
|
|
}
|
2007-01-25 23:46:05 +08:00
|
|
|
|
|
|
|
public static boolean isMultiline(String s) {
|
2007-01-26 21:12:40 +08:00
|
|
|
if(s==null) return false;
|
2007-01-25 23:46:05 +08:00
|
|
|
return s.indexOf('\r')>=0 || s.indexOf('\n')>=0;
|
|
|
|
}
|
2007-03-04 13:53:34 +08:00
|
|
|
|
|
|
|
public static String encode(String s) {
|
|
|
|
return Util.encode(s);
|
|
|
|
}
|
2007-03-11 02:03:02 +08:00
|
|
|
|
2007-04-17 09:03:55 +08:00
|
|
|
public static String escape(String s) {
|
|
|
|
return Util.escape(s);
|
|
|
|
}
|
|
|
|
|
2007-04-17 09:15:20 +08:00
|
|
|
public static String xmlEscape(String s) {
|
|
|
|
return Util.xmlEscape(s);
|
|
|
|
}
|
|
|
|
|
2008-01-04 09:32:30 +08:00
|
|
|
public static void checkPermission(Permission permission) throws IOException, ServletException {
|
|
|
|
Hudson.getInstance().getACL().checkPermission(permission);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the current user has the given permission.
|
2008-01-04 09:58:09 +08:00
|
|
|
*
|
|
|
|
* @param permission
|
|
|
|
* If null, returns true. This defaulting is convenient in making the use of this method terse.
|
2008-01-04 09:32:30 +08:00
|
|
|
*/
|
|
|
|
public static boolean hasPermission(Permission permission) throws IOException, ServletException {
|
2008-01-04 09:58:09 +08:00
|
|
|
return permission==null || Hudson.getInstance().getACL().hasPermission(permission);
|
2008-01-04 09:32:30 +08:00
|
|
|
}
|
|
|
|
|
2007-12-18 14:55:31 +08:00
|
|
|
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.
|
2007-12-27 12:12:48 +08:00
|
|
|
if(required!=null && !Hudson.adminCheck(req,rsp)) {
|
2007-07-30 01:53:56 +08:00
|
|
|
// check failed. commit the FORBIDDEN response, then abort.
|
|
|
|
rsp.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
|
|
|
rsp.getOutputStream().close();
|
2007-03-11 02:03:02 +08:00
|
|
|
throw new ServletException("Unauthorized access");
|
|
|
|
}
|
2007-12-18 14:55:31 +08:00
|
|
|
|
|
|
|
// make sure the user owns the necessary permission to access this page.
|
2007-12-20 14:35:28 +08:00
|
|
|
if(permission!=null)
|
2008-01-04 09:32:30 +08:00
|
|
|
checkPermission(permission);
|
2007-03-11 02:03:02 +08:00
|
|
|
}
|
2007-03-11 06:43:16 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Infers the hudson installation URL from the given request.
|
|
|
|
*/
|
|
|
|
public static String inferHudsonURL(StaplerRequest req) {
|
2007-09-03 08:12:13 +08:00
|
|
|
String rootUrl = Hudson.getInstance().getRootUrl();
|
|
|
|
if(rootUrl !=null)
|
|
|
|
// prefer the one explicitly configured, to work with load-balancer, frontend, etc.
|
|
|
|
return rootUrl;
|
2007-03-11 06:43:16 +08:00
|
|
|
StringBuilder buf = new StringBuilder();
|
|
|
|
buf.append(req.getScheme()).append("://");
|
|
|
|
buf.append(req.getServerName());
|
|
|
|
if(req.getLocalPort()!=80)
|
|
|
|
buf.append(':').append(req.getLocalPort());
|
2007-08-11 13:29:33 +08:00
|
|
|
buf.append(req.getContextPath()).append('/');
|
2007-03-11 06:43:16 +08:00
|
|
|
return buf.toString();
|
|
|
|
}
|
2007-03-11 08:56:41 +08:00
|
|
|
|
|
|
|
public static List<JobPropertyDescriptor> getJobPropertyDescriptors(Class<? extends Job> clazz) {
|
|
|
|
return JobPropertyDescriptor.getPropertyDescriptors(clazz);
|
|
|
|
}
|
2007-03-26 07:50:35 +08:00
|
|
|
|
2007-10-29 07:39:31 +08:00
|
|
|
public static List<Descriptor<BuildWrapper>> getBuildWrapperDescriptors(AbstractProject<?,?> project) {
|
|
|
|
return BuildWrappers.getFor(project);
|
|
|
|
}
|
|
|
|
|
2007-12-06 13:53:00 +08:00
|
|
|
public static List<Descriptor<SecurityRealm>> getSecurityRealmDescriptors() {
|
2007-12-05 15:09:29 +08:00
|
|
|
return SecurityRealm.LIST;
|
|
|
|
}
|
|
|
|
|
2007-12-06 13:53:00 +08:00
|
|
|
public static List<Descriptor<AuthorizationStrategy>> getAuthorizationStrategyDescriptors() {
|
|
|
|
return AuthorizationStrategy.LIST;
|
|
|
|
}
|
|
|
|
|
2007-10-29 07:20:01 +08:00
|
|
|
public static List<Descriptor<Builder>> getBuilderDescriptors(AbstractProject<?,?> project) {
|
2008-02-03 04:14:15 +08:00
|
|
|
return BuildStepDescriptor.filter(BuildStep.BUILDERS, project.getClass());
|
2007-10-29 07:20:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public static List<Descriptor<Publisher>> getPublisherDescriptors(AbstractProject<?,?> project) {
|
2008-02-03 04:14:15 +08:00
|
|
|
return BuildStepDescriptor.filter(BuildStep.PUBLISHERS, project.getClass());
|
2007-10-29 07:20:01 +08:00
|
|
|
}
|
|
|
|
|
2007-03-26 07:50:35 +08:00
|
|
|
/**
|
|
|
|
* 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("/"))
|
|
|
|
return name.substring(1);
|
|
|
|
else
|
|
|
|
return "images/24x24/"+name;
|
|
|
|
}
|
2007-03-27 03:37:49 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Works like JSTL build-in size(x) function,
|
|
|
|
* but handle null gracefully.
|
|
|
|
*/
|
2007-03-27 03:40:34 +08:00
|
|
|
public static int size2(Object o) throws Exception {
|
2007-03-27 03:37:49 +08:00
|
|
|
if(o==null) return 0;
|
2007-10-23 02:28:11 +08:00
|
|
|
return ASTSizeFunction.sizeOf(o,Introspector.getUberspect());
|
2007-03-27 03:37:49 +08:00
|
|
|
}
|
2007-04-08 10:10:13 +08:00
|
|
|
|
|
|
|
public static ExecutedMojo.Cache createExecutedMojoCache() {
|
|
|
|
return new ExecutedMojo.Cache();
|
|
|
|
}
|
2007-04-11 13:11:12 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
StaplerRequest request = Stapler.getCurrentRequest();
|
|
|
|
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;
|
|
|
|
|
|
|
|
Item i=p;
|
|
|
|
String url = "";
|
|
|
|
while(true) {
|
|
|
|
ItemGroup ig = i.getParent();
|
|
|
|
url = i.getShortUrl()+url;
|
|
|
|
|
|
|
|
if(ig==Hudson.getInstance()) {
|
|
|
|
assert i instanceof TopLevelItem;
|
|
|
|
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;
|
|
|
|
} else {
|
|
|
|
// otherwise return a path from the root Hudson
|
|
|
|
return request.getContextPath()+'/'+p.getUrl();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
path = ancestors.get(ig);
|
|
|
|
if(path!=null) return path+'/'+url;
|
|
|
|
|
|
|
|
assert ig instanceof Item; // if not, ig must have been the Hudson instance
|
|
|
|
i = (Item) ig;
|
|
|
|
}
|
|
|
|
}
|
2007-04-13 13:54:45 +08:00
|
|
|
|
2007-04-13 14:19:00 +08:00
|
|
|
public static Map<Thread,StackTraceElement[]> dumpAllThreads() {
|
2007-04-13 13:54:45 +08:00
|
|
|
return Thread.getAllStackTraces();
|
|
|
|
}
|
2007-04-13 14:09:38 +08:00
|
|
|
|
2007-04-13 14:19:00 +08:00
|
|
|
public static ThreadInfo[] getThreadInfos() {
|
2007-04-13 14:09:38 +08:00
|
|
|
ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
|
|
|
|
return mbean.getThreadInfo(mbean.getAllThreadIds(),mbean.isObjectMonitorUsageSupported(),mbean.isSynchronizerUsageSupported());
|
|
|
|
}
|
|
|
|
|
2007-07-30 03:05:37 +08:00
|
|
|
/**
|
|
|
|
* Are we running on JRE6 or above?
|
|
|
|
*/
|
|
|
|
public static boolean isMustangOrAbove() {
|
|
|
|
try {
|
|
|
|
System.console();
|
|
|
|
return true;
|
|
|
|
} catch(LinkageError e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-04-13 14:09:38 +08:00
|
|
|
// ThreadInfo.toString() truncates the stack trace by first 8, so needed my own version
|
2007-04-13 14:19:00 +08:00
|
|
|
public static String dumpThreadInfo(ThreadInfo ti) {
|
2007-04-13 14:09:38 +08:00
|
|
|
StringBuilder sb = new StringBuilder("\"" + ti.getThreadName() + "\"" +
|
|
|
|
" Id=" + ti.getThreadId() + " " +
|
|
|
|
ti.getThreadState());
|
|
|
|
if (ti.getLockName() != null) {
|
|
|
|
sb.append(" on " + ti.getLockName());
|
|
|
|
}
|
|
|
|
if (ti.getLockOwnerName() != null) {
|
|
|
|
sb.append(" owned by \"" + ti.getLockOwnerName() +
|
|
|
|
"\" Id=" + ti.getLockOwnerId());
|
|
|
|
}
|
|
|
|
if (ti.isSuspended()) {
|
|
|
|
sb.append(" (suspended)");
|
|
|
|
}
|
|
|
|
if (ti.isInNative()) {
|
|
|
|
sb.append(" (in native)");
|
|
|
|
}
|
|
|
|
sb.append('\n');
|
|
|
|
StackTraceElement[] stackTrace = ti.getStackTrace();
|
|
|
|
for (int i=0; i < stackTrace.length; i++) {
|
|
|
|
StackTraceElement ste = stackTrace[i];
|
|
|
|
sb.append("\tat " + ste.toString());
|
|
|
|
sb.append('\n');
|
|
|
|
if (i == 0 && ti.getLockInfo() != null) {
|
|
|
|
Thread.State ts = ti.getThreadState();
|
|
|
|
switch (ts) {
|
|
|
|
case BLOCKED:
|
|
|
|
sb.append("\t- blocked on " + ti.getLockInfo());
|
|
|
|
sb.append('\n');
|
|
|
|
break;
|
|
|
|
case WAITING:
|
|
|
|
sb.append("\t- waiting on " + ti.getLockInfo());
|
|
|
|
sb.append('\n');
|
|
|
|
break;
|
|
|
|
case TIMED_WAITING:
|
|
|
|
sb.append("\t- waiting on " + ti.getLockInfo());
|
|
|
|
sb.append('\n');
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (MonitorInfo mi : ti.getLockedMonitors()) {
|
|
|
|
if (mi.getLockedStackDepth() == i) {
|
|
|
|
sb.append("\t- locked " + mi);
|
|
|
|
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();
|
|
|
|
}
|
2007-06-24 14:51:59 +08:00
|
|
|
|
|
|
|
public static <T> Collection<T> emptyList() {
|
|
|
|
return Collections.emptyList();
|
|
|
|
}
|
2007-07-10 03:19:35 +08:00
|
|
|
|
|
|
|
public static String jsStringEscape(String s) {
|
|
|
|
StringBuilder buf = new StringBuilder();
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return buf.toString();
|
|
|
|
}
|
2007-08-05 03:33:27 +08:00
|
|
|
|
2007-08-05 22:48:26 +08:00
|
|
|
public static String getVersion() {
|
|
|
|
return Hudson.VERSION;
|
|
|
|
}
|
|
|
|
|
2007-08-06 00:18:19 +08:00
|
|
|
/**
|
|
|
|
* Resoruce path prefix.
|
|
|
|
*/
|
|
|
|
public static String getResourcePath() {
|
|
|
|
return Hudson.RESOURCE_PATH;
|
|
|
|
}
|
2007-09-01 22:33:10 +08:00
|
|
|
|
2007-12-25 06:47:19 +08:00
|
|
|
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;
|
|
|
|
|
2008-04-09 12:35:31 +08:00
|
|
|
StringBuilder buf = new StringBuilder(Stapler.getCurrentRequest().getContextPath());
|
2007-12-25 06:47:19 +08:00
|
|
|
buf.append(Hudson.VIEW_RESOURCE_PATH).append('/');
|
|
|
|
buf.append(clazz.getName().replace('.','/').replace('$','/'));
|
|
|
|
buf.append('/').append(path);
|
|
|
|
|
|
|
|
return buf.toString();
|
|
|
|
}
|
|
|
|
|
2007-09-01 22:33:10 +08:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
return b;
|
|
|
|
}
|
2007-09-23 00:39:31 +08:00
|
|
|
|
2007-10-28 12:11:11 +08:00
|
|
|
/**
|
|
|
|
* If the value exists, return that value. Otherwise return the default value.
|
|
|
|
* <p>
|
|
|
|
* This method is useful for supplying a default value to a form field.
|
|
|
|
*
|
|
|
|
* @since 1.150
|
|
|
|
*/
|
|
|
|
public static Object defaulted(Object value, Object defaultValue) {
|
|
|
|
return value!=null ? value : defaultValue;
|
|
|
|
}
|
|
|
|
|
2007-09-23 00:39:31 +08:00
|
|
|
public static String printThrowable(Throwable t) {
|
|
|
|
StringWriter sw = new StringWriter();
|
|
|
|
t.printStackTrace(new PrintWriter(sw));
|
|
|
|
return sw.toString();
|
|
|
|
}
|
2007-09-27 12:16:44 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2007-11-13 10:36:15 +08:00
|
|
|
/**
|
|
|
|
* 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";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "Unknown";
|
|
|
|
}
|
|
|
|
|
2007-09-27 12:16:44 +08:00
|
|
|
private static final Pattern LINE_END = Pattern.compile("\r?\n");
|
2007-12-17 14:29:41 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if the current user is anonymous.
|
|
|
|
*/
|
|
|
|
public static boolean isAnonymous() {
|
|
|
|
return Hudson.getAuthentication() instanceof AnonymousAuthenticationToken;
|
|
|
|
}
|
2007-12-25 01:06:32 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 = ExpressionFactory2.CURRENT_CONTEXT.get();
|
|
|
|
assert context!=null;
|
|
|
|
return context;
|
|
|
|
}
|
2008-03-05 10:08:38 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
return base;
|
|
|
|
}
|
2008-03-21 10:00:54 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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) {
|
|
|
|
String urlName = action.getUrlName();
|
|
|
|
|
|
|
|
if(SCHEME.matcher(urlName).matches())
|
|
|
|
return urlName; // absolute URL
|
|
|
|
else
|
|
|
|
// relative URL name
|
|
|
|
return Stapler.getCurrentRequest().getContextPath()+'/'+itUrl+urlName;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final Pattern SCHEME = Pattern.compile("[a-z]+://.+");
|
2006-11-06 05:16:01 +08:00
|
|
|
}
|