File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\Functions.java.html

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
5 * Yahoo! Inc., Stephen Connolly, Tom Huybrechts, Alan Harder, Manufacture
6 * Francaise des Pneumatiques Michelin, Romain Seguy
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26 package hudson;
27
28 import com.google.common.base.Predicate;
29 import com.google.common.base.Predicates;
30 import hudson.cli.CLICommand;
31 import hudson.console.ConsoleAnnotationDescriptor;
32 import hudson.console.ConsoleAnnotatorFactory;
33 import hudson.model.*;
34 import hudson.model.ParameterDefinition.ParameterDescriptor;
35 import hudson.search.SearchableModelObject;
36 import hudson.security.AccessControlled;
37 import hudson.security.AuthorizationStrategy;
38 import hudson.security.GlobalSecurityConfiguration;
39 import hudson.security.Permission;
40 import hudson.security.SecurityRealm;
41 import hudson.security.captcha.CaptchaSupport;
42 import hudson.security.csrf.CrumbIssuer;
43 import hudson.slaves.Cloud;
44 import hudson.slaves.ComputerLauncher;
45 import hudson.slaves.NodeProperty;
46 import hudson.slaves.NodePropertyDescriptor;
47 import hudson.slaves.RetentionStrategy;
48 import hudson.tasks.BuildStepDescriptor;
49 import hudson.tasks.BuildWrapper;
50 import hudson.tasks.BuildWrappers;
51 import hudson.tasks.Builder;
52 import hudson.tasks.Publisher;
53 import hudson.tasks.UserAvatarResolver;
54 import hudson.util.Area;
55 import hudson.util.Iterators;
56 import hudson.scm.SCM;
57 import hudson.scm.SCMDescriptor;
58 import hudson.util.Secret;
59 import hudson.views.MyViewsTabBar;
60 import hudson.views.ViewsTabBar;
61 import hudson.widgets.RenderOnDemandClosure;
62 import jenkins.model.GlobalConfiguration;
63 import jenkins.model.GlobalConfigurationCategory;
64 import jenkins.model.GlobalConfigurationCategory.Unclassified;
65 import jenkins.model.Jenkins;
66 import jenkins.model.ModelObjectWithContextMenu;
67 import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
68 import org.apache.commons.jelly.JellyContext;
69 import org.apache.commons.jelly.JellyTagException;
70 import org.apache.commons.jelly.Script;
71 import org.apache.commons.jelly.XMLOutput;
72 import org.apache.commons.jexl.parser.ASTSizeFunction;
73 import org.apache.commons.jexl.util.Introspector;
74 import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
75 import org.jvnet.tiger_types.Types;
76 import org.kohsuke.stapler.Ancestor;
77 import org.kohsuke.stapler.Stapler;
78 import org.kohsuke.stapler.StaplerRequest;
79 import org.kohsuke.stapler.StaplerResponse;
80 import org.kohsuke.stapler.jelly.InternationalizedStringExpression.RawHtmlArgument;
81
82 import javax.servlet.ServletException;
83 import javax.servlet.http.Cookie;
84 import javax.servlet.http.HttpServletRequest;
85 import javax.servlet.http.HttpServletResponse;
86 import java.io.File;
87 import java.io.IOException;
88 import java.io.PrintWriter;
89 import java.io.StringWriter;
90 import java.io.UnsupportedEncodingException;
91 import java.lang.management.LockInfo;
92 import java.lang.management.ManagementFactory;
93 import java.lang.management.MonitorInfo;
94 import java.lang.management.ThreadInfo;
95 import java.lang.management.ThreadMXBean;
96 import java.lang.reflect.Type;
97 import java.lang.reflect.ParameterizedType;
98 import java.net.MalformedURLException;
99 import java.net.URI;
100 import java.net.URISyntaxException;
101 import java.net.URL;
102 import java.net.URLDecoder;
103 import java.text.DecimalFormat;
104 import java.util.ArrayList;
105 import java.util.Arrays;
106 import java.util.Calendar;
107 import java.util.Collection;
108 import java.util.Collections;
109 import java.util.Comparator;
110 import java.util.ConcurrentModificationException;
111 import java.util.Enumeration;
112 import java.util.HashMap;
113 import java.util.List;
114 import java.util.Locale;
115 import java.util.Map;
116 import java.util.SortedMap;
117 import java.util.TreeMap;
118 import java.util.Date;
119 import java.util.logging.Level;
120 import java.util.logging.LogManager;
121 import java.util.logging.LogRecord;
122 import java.util.logging.Logger;
123 import java.util.logging.SimpleFormatter;
124 import java.util.regex.Pattern;
125 import org.apache.commons.lang.StringUtils;
126
127 /**
128 * Utility functions used in views.
129 *
130 * <p>
131 * An instance of this class is created for each request and made accessible
132 * from view pages via the variable 'h' (h stands for Hudson.)
133 *
134 * @author Kohsuke Kawaguchi
135 */
136 public class Functions {
137 private static volatile int globalIota = 0;
138 private int iota;
139
140 public Functions() {
141 iota = globalIota;
142 // concurrent requests can use the same ID --- we are just trying to
143 // prevent the same user from seeing the same ID repeatedly.
144 globalIota+=1000;
145 }
146
147 /**
148 * Generates an unique ID.
149 */
150 public String generateId() {
151 return "id"+iota++;
152 }
153
154 public static boolean isModel(Object o) {
155 return o instanceof ModelObject;
156 }
157
158 public static boolean isModelWithContextMenu(Object o) {
159 return o instanceof ModelObjectWithContextMenu;
160 }
161
162 public static String xsDate(Calendar cal) {
163 return Util.XS_DATETIME_FORMATTER.format(cal.getTime());
164 }
165
166 public static String rfc822Date(Calendar cal) {
167 return Util.RFC822_DATETIME_FORMATTER.format(cal.getTime());
168 }
169
170 public static void initPageVariables(JellyContext context) {
171 String rootURL = Stapler.getCurrentRequest().getContextPath();
172
173 Functions h = new Functions();
174 context.setVariable("h", h);
175
176
177 // The path starts with a "/" character but does not end with a "/" character.
178 context.setVariable("rootURL", rootURL);
179
180 /*
181 load static resources from the path dedicated to a specific version.
182 This "/static/VERSION/abc/def.ghi" path is interpreted by stapler to be
183 the same thing as "/abc/def.ghi", but this avoids the stale cache
184 problem when the user upgrades to new Jenkins. Stapler also sets a long
185 future expiration dates for such static resources.
186 */
187 context.setVariable("resURL",rootURL+getResourcePath());
188 context.setVariable("imagesURL",rootURL+getResourcePath()+"/images");
189 }
190
191 /**
192 * Given {@code c=MyList (extends ArrayList<Foo>), base=List}, compute the parameterization of 'base'
193 * that's assignable from 'c' (in this case {@code List<Foo>}), and return its n-th type parameter
194 * (n=0 would return {@code Foo}).
195 *
196 * <p>
197 * This method is useful for doing type arithmetic.
198 *
199 * @throws AssertionError
200 * if c' is not parameterized.
201 */
202 public static <B> Class getTypeParameter(Class<? extends B> c, Class<B> base, int n) {
203 Type parameterization = Types.getBaseClass(c,base);
204 if (parameterization instanceof ParameterizedType) {
205 ParameterizedType pt = (ParameterizedType) parameterization;
206 return Types.erasure(Types.getTypeArgument(pt,n));
207 } else {
208 throw new AssertionError(c+" doesn't properly parameterize "+base);
209 }
210 }
211
212 public JDK.DescriptorImpl getJDKDescriptor() {
213 return Jenkins.getInstance().getDescriptorByType(JDK.DescriptorImpl.class);
214 }
215
216 /**
217 * Prints the integer as a string that represents difference,
218 * like "-5", "+/-0", "+3".
219 */
220 public static String getDiffString(int i) {
221 if(i==0) return "\u00B10"; // +/-0
222 String s = Integer.toString(i);
223 if(i>0) return "+"+s;
224 else return s;
225 }
226
227 /**
228 * {@link #getDiffString(int)} that doesn't show anything for +/-0
229 */
230 public static String getDiffString2(int i) {
231 if(i==0) return "";
232 String s = Integer.toString(i);
233 if(i>0) return "+"+s;
234 else return s;
235 }
236
237 /**
238 * {@link #getDiffString2(int)} that puts the result into prefix and suffix
239 * if there's something to print
240 */
241 public static String getDiffString2(String prefix, int i, String suffix) {
242 if(i==0) return "";
243 String s = Integer.toString(i);
244 if(i>0) return prefix+"+"+s+suffix;
245 else return prefix+s+suffix;
246 }
247
248 /**
249 * Adds the proper suffix.
250 */
251 public static String addSuffix(int n, String singular, String plural) {
252 StringBuilder buf = new StringBuilder();
253 buf.append(n).append(' ');
254 if(n==1)
255 buf.append(singular);
256 else
257 buf.append(plural);
258 return buf.toString();
259 }
260
261 public static RunUrl decompose(StaplerRequest req) {
262 List<Ancestor> ancestors = req.getAncestors();
263
264 // find the first and last Run instances
265 Ancestor f=null,l=null;
266 for (Ancestor anc : ancestors) {
267 if(anc.getObject() instanceof Run) {
268 if(f==null) f=anc;
269 l=anc;
270 }
271 }
272 if(l==null) return null; // there was no Run object
273
274 String head = f.getPrev().getUrl()+'/';
275 String base = l.getUrl();
276
277 String reqUri = req.getOriginalRequestURI();
278 // Find "rest" or URI by removing N path components.
279 // Not using reqUri.substring(f.getUrl().length()) to avoid mismatches due to
280 // url-encoding or extra slashes. Former may occur in Tomcat (despite the spec saying
281 // this string is not decoded, Tomcat apparently decodes this string. You see ' '
282 // instead of '%20', which is what the browser has sent), latter may occur in some
283 // proxy or URL-rewriting setups where extra slashes are inadvertently added.
284 String furl = f.getUrl();
285 int slashCount = 0;
286 // Count components in ancestor URL
287 for (int i = furl.indexOf('/'); i >= 0; i = furl.indexOf('/', i + 1)) slashCount++;
288 // Remove that many from request URL, ignoring extra slashes
289 String rest = reqUri.replaceFirst("(?:/+[^/]*){" + slashCount + "}", "");
290
291 return new RunUrl( (Run) f.getObject(), head, base, rest);
292 }
293
294 /**
295 * If we know the user's screen resolution, return it. Otherwise null.
296 * @since 1.213
297 */
298 public static Area getScreenResolution() {
299 Cookie res = Functions.getCookie(Stapler.getCurrentRequest(),"screenResolution");
300 if(res!=null)
301 return Area.parse(res.getValue());
302 return null;
303 }
304
305 /**
306 * URL decomposed for easier computation of relevant URLs.
307 *
308 * <p>
309 * The decomposed URL will be of the form:
310 * <pre>
311 * aaaaaa/524/bbbbb/cccc
312 * -head-| N |---rest---
313 * ----- base -----|
314 * </pre>
315 *
316 * <p>
317 * The head portion is the part of the URL from the {@link jenkins.model.Jenkins}
318 * object to the first {@link Run} subtype. When "next/prev build"
319 * is chosen, this part remains intact.
320 *
321 * <p>
322 * The <tt>524</tt> is the path from {@link Job} to {@link Run}.
323 *
324 * <p>
325 * The <tt>bbb</tt> portion is the path after that till the last
326 * {@link Run} subtype. The <tt>ccc</tt> portion is the part
327 * after that.
328 */
329 public static final class RunUrl {
330 private final String head, base, rest;
331 private final Run run;
332
333
334 public RunUrl(Run run, String head, String base, String rest) {
335 this.run = run;
336 this.head = head;
337 this.base = base;
338 this.rest = rest;
339 }
340
341 public String getBaseUrl() {
342 return base;
343 }
344
345 /**
346 * Returns the same page in the next build.
347 */
348 public String getNextBuildUrl() {
349 return getUrl(run.getNextBuild());
350 }
351
352 /**
353 * Returns the same page in the previous build.
354 */
355 public String getPreviousBuildUrl() {
356 return getUrl(run.getPreviousBuild());
357 }
358
359 private String getUrl(Run n) {
360 if(n ==null)
361 return null;
362 else {
363 return head+n.getNumber()+rest;
364 }
365 }
366 }
367
368 public static Node.Mode[] getNodeModes() {
369 return Node.Mode.values();
370 }
371
372 public static String getProjectListString(List<Project> projects) {
373 return Items.toNameList(projects);
374 }
375
376 /**
377 * @deprecated as of 1.294
378 * JEXL now supports the real ternary operator "x?y:z", so this work around
379 * is no longer necessary.
380 */
381 public static Object ifThenElse(boolean cond, Object thenValue, Object elseValue) {
382 return cond ? thenValue : elseValue;
383 }
384
385 public static String appendIfNotNull(String text, String suffix, String nullText) {
386 return text == null ? nullText : text + suffix;
387 }
388
389 public static Map getSystemProperties() {
390 return new TreeMap<Object,Object>(System.getProperties());
391 }
392
393 public static Map getEnvVars() {
394 return new TreeMap<String,String>(EnvVars.masterEnvVars);
395 }
396
397 public static boolean isWindows() {
398 return File.pathSeparatorChar==';';
399 }
400
401 public static List<LogRecord> getLogRecords() {
402 return Jenkins.logRecords;
403 }
404
405 public static String printLogRecord(LogRecord r) {
406 return formatter.format(r);
407 }
408
409 public static Cookie getCookie(HttpServletRequest req,String name) {
410 Cookie[] cookies = req.getCookies();
411 if(cookies!=null) {
412 for (Cookie cookie : cookies) {
413 if(cookie.getName().equals(name)) {
414 return cookie;
415 }
416 }
417 }
418 return null;
419 }
420
421 public static String getCookie(HttpServletRequest req,String name, String defaultValue) {
422 Cookie c = getCookie(req, name);
423 if(c==null || c.getValue()==null) return defaultValue;
424 return c.getValue();
425 }
426
427 /**
428 * Gets the suffix to use for YUI JavaScript.
429 */
430 public static String getYuiSuffix() {
431 return DEBUG_YUI ? "debug" : "min";
432 }
433
434 /**
435 * Set to true if you need to use the debug version of YUI.
436 */
437 public static boolean DEBUG_YUI = Boolean.getBoolean("debug.YUI");
438
439 /**
440 * Creates a sub map by using the given range (both ends inclusive).
441 */
442 public static <V> SortedMap<Integer,V> filter(SortedMap<Integer,V> map, String from, String to) {
443 if(from==null && to==null) return map;
444 if(to==null)
445 return map.headMap(Integer.parseInt(from)-1);
446 if(from==null)
447 return map.tailMap(Integer.parseInt(to));
448
449 return map.subMap(Integer.parseInt(to),Integer.parseInt(from)-1);
450 }
451
452 private static final SimpleFormatter formatter = new SimpleFormatter();
453
454 /**
455 * Used by <tt>layout.jelly</tt> to control the auto refresh behavior.
456 *
457 * @param noAutoRefresh
458 * On certain pages, like a page with forms, will have annoying interference
459 * with auto refresh. On those pages, disable auto-refresh.
460 */
461 public static void configureAutoRefresh(HttpServletRequest request, HttpServletResponse response, boolean noAutoRefresh) {
462 if(noAutoRefresh)
463 return;
464
465 String param = request.getParameter("auto_refresh");
466 boolean refresh = isAutoRefresh(request);
467 if (param != null) {
468 refresh = Boolean.parseBoolean(param);
469 Cookie c = new Cookie("hudson_auto_refresh", Boolean.toString(refresh));
470 // Need to set path or it will not stick from e.g. a project page to the dashboard.
471 // Using request.getContextPath() might work but it seems simpler to just use the hudson_ prefix
472 // to avoid conflicts with any other web apps that might be on the same machine.
473 c.setPath("/");
474 c.setMaxAge(60*60*24*30); // persist it roughly for a month
475 response.addCookie(c);
476 }
477 if (refresh) {
478 response.addHeader("Refresh", System.getProperty("hudson.Functions.autoRefreshSeconds", "10"));
479 }
480 }
481
482 public static boolean isAutoRefresh(HttpServletRequest request) {
483 String param = request.getParameter("auto_refresh");
484 if (param != null) {
485 return Boolean.parseBoolean(param);
486 }
487 Cookie[] cookies = request.getCookies();
488 if(cookies==null)
489 return false; // when API design messes it up, we all suffer
490
491 for (Cookie c : cookies) {
492 if (c.getName().equals("hudson_auto_refresh")) {
493 return Boolean.parseBoolean(c.getValue());
494 }
495 }
496 return false;
497 }
498
499 /**
500 * Finds the given object in the ancestor list and returns its URL.
501 * This is used to determine the "current" URL assigned to the given object,
502 * so that one can compute relative URLs from it.
503 */
504 public static String getNearestAncestorUrl(StaplerRequest req,Object it) {
505 List list = req.getAncestors();
506 for( int i=list.size()-1; i>=0; i-- ) {
507 Ancestor anc = (Ancestor) list.get(i);
508 if(anc.getObject()==it)
509 return anc.getUrl();
510 }
511 return null;
512 }
513
514 /**
515 * Finds the inner-most {@link SearchableModelObject} in scope.
516 */
517 public static String getSearchURL() {
518 List list = Stapler.getCurrentRequest().getAncestors();
519 for( int i=list.size()-1; i>=0; i-- ) {
520 Ancestor anc = (Ancestor) list.get(i);
521 if(anc.getObject() instanceof SearchableModelObject)
522 return anc.getUrl()+"/search/";
523 }
524 return null;
525 }
526
527 public static String appendSpaceIfNotNull(String n) {
528 if(n==null) return null;
529 else return n+' ';
530 }
531
532 /**
533 * One nbsp per 10 pixels in given size, which may be a plain number or "NxN"
534 * (like an iconSize). Useful in a sortable table heading.
535 */
536 public static String nbspIndent(String size) {
537 int i = size.indexOf('x');
538 i = Integer.parseInt(i > 0 ? size.substring(0, i) : size) / 10;
539 StringBuilder buf = new StringBuilder(30);
540 for (int j = 0; j < i; j++)
541 buf.append("&nbsp;");
542 return buf.toString();
543 }
544
545 public static String getWin32ErrorMessage(IOException e) {
546 return Util.getWin32ErrorMessage(e);
547 }
548
549 public static boolean isMultiline(String s) {
550 if(s==null) return false;
551 return s.indexOf('\r')>=0 || s.indexOf('\n')>=0;
552 }
553
554 public static String encode(String s) {
555 return Util.encode(s);
556 }
557
558 public static String escape(String s) {
559 return Util.escape(s);
560 }
561
562 public static String xmlEscape(String s) {
563 return Util.xmlEscape(s);
564 }
565
566 public static String xmlUnescape(String s) {
567 return s.replace("&lt;","<").replace("&gt;",">").replace("&amp;","&");
568 }
569
570 public static String htmlAttributeEscape(String text) {
571 StringBuilder buf = new StringBuilder(text.length()+64);
572 for( int i=0; i<text.length(); i++ ) {
573 char ch = text.charAt(i);
574 if(ch=='<')
575 buf.append("&lt;");
576 else
577 if(ch=='>')
578 buf.append("&gt;");
579 else
580 if(ch=='&')
581 buf.append("&amp;");
582 else
583 if(ch=='"')
584 buf.append("&quot;");
585 else
586 if(ch=='\'')
587 buf.append("&#39;");
588 else
589 buf.append(ch);
590 }
591 return buf.toString();
592 }
593
594 public static void checkPermission(Permission permission) throws IOException, ServletException {
595 checkPermission(Jenkins.getInstance(),permission);
596 }
597
598 public static void checkPermission(AccessControlled object, Permission permission) throws IOException, ServletException {
599 if (permission != null) {
600 object.checkPermission(permission);
601 }
602 }
603
604 /**
605 * This version is so that the 'checkPermission' on <tt>layout.jelly</tt>
606 * degrades gracefully if "it" is not an {@link AccessControlled} object.
607 * Otherwise it will perform no check and that problem is hard to notice.
608 */
609 public static void checkPermission(Object object, Permission permission) throws IOException, ServletException {
610 if (permission == null)
611 return;
612
613 if (object instanceof AccessControlled)
614 checkPermission((AccessControlled) object,permission);
615 else {
616 List<Ancestor> ancs = Stapler.getCurrentRequest().getAncestors();
617 for(Ancestor anc : Iterators.reverse(ancs)) {
618 Object o = anc.getObject();
619 if (o instanceof AccessControlled) {
620 checkPermission((AccessControlled) o,permission);
621 return;
622 }
623 }
624 checkPermission(Jenkins.getInstance(),permission);
625 }
626 }
627
628 /**
629 * Returns true if the current user has the given permission.
630 *
631 * @param permission
632 * If null, returns true. This defaulting is convenient in making the use of this method terse.
633 */
634 public static boolean hasPermission(Permission permission) throws IOException, ServletException {
635 return hasPermission(Jenkins.getInstance(),permission);
636 }
637
638 /**
639 * This version is so that the 'hasPermission' can degrade gracefully
640 * if "it" is not an {@link AccessControlled} object.
641 */
642 public static boolean hasPermission(Object object, Permission permission) throws IOException, ServletException {
643 if (permission == null)
644 return true;
645 if (object instanceof AccessControlled)
646 return ((AccessControlled)object).hasPermission(permission);
647 else {
648 List<Ancestor> ancs = Stapler.getCurrentRequest().getAncestors();
649 for(Ancestor anc : Iterators.reverse(ancs)) {
650 Object o = anc.getObject();
651 if (o instanceof AccessControlled) {
652 return ((AccessControlled)o).hasPermission(permission);
653 }
654 }
655 return Jenkins.getInstance().hasPermission(permission);
656 }
657 }
658
659 public static void adminCheck(StaplerRequest req, StaplerResponse rsp, Object required, Permission permission) throws IOException, ServletException {
660 // this is legacy --- all views should be eventually converted to
661 // the permission based model.
662 if(required!=null && !Hudson.adminCheck(req, rsp)) {
663 // check failed. commit the FORBIDDEN response, then abort.
664 rsp.setStatus(HttpServletResponse.SC_FORBIDDEN);
665 rsp.getOutputStream().close();
666 throw new ServletException("Unauthorized access");
667 }
668
669 // make sure the user owns the necessary permission to access this page.
670 if(permission!=null)
671 checkPermission(permission);
672 }
673
674 /**
675 * Infers the hudson installation URL from the given request.
676 */
677 public static String inferHudsonURL(StaplerRequest req) {
678 String rootUrl = Jenkins.getInstance().getRootUrl();
679 if(rootUrl !=null)
680 // prefer the one explicitly configured, to work with load-balancer, frontend, etc.
681 return rootUrl;
682 StringBuilder buf = new StringBuilder();
683 buf.append(req.getScheme()).append("://");
684 buf.append(req.getServerName());
685 if(! (req.getScheme().equals("http") && req.getLocalPort()==80 || req.getScheme().equals("https") && req.getLocalPort()==443))
686 buf.append(':').append(req.getLocalPort());
687 buf.append(req.getContextPath()).append('/');
688 return buf.toString();
689 }
690
691 /**
692 * Returns the link to be displayed in the footer of the UI.
693 */
694 public static String getFooterURL() {
695 if(footerURL == null) {
696 footerURL = System.getProperty("hudson.footerURL");
697 if(StringUtils.isBlank(footerURL)) {
698 footerURL = "http://jenkins-ci.org/";
699 }
700 }
701 return footerURL;
702 }
703 private static String footerURL = null;
704
705 public static List<JobPropertyDescriptor> getJobPropertyDescriptors(Class<? extends Job> clazz) {
706 return JobPropertyDescriptor.getPropertyDescriptors(clazz);
707 }
708
709 public static List<Descriptor<BuildWrapper>> getBuildWrapperDescriptors(AbstractProject<?,?> project) {
710 return BuildWrappers.getFor(project);
711 }
712
713 public static List<Descriptor<SecurityRealm>> getSecurityRealmDescriptors() {
714 return SecurityRealm.all();
715 }
716
717 public static List<Descriptor<AuthorizationStrategy>> getAuthorizationStrategyDescriptors() {
718 return AuthorizationStrategy.all();
719 }
720
721 public static List<Descriptor<Builder>> getBuilderDescriptors(AbstractProject<?,?> project) {
722 return BuildStepDescriptor.filter(Builder.all(), project.getClass());
723 }
724
725 public static List<Descriptor<Publisher>> getPublisherDescriptors(AbstractProject<?,?> project) {
726 return BuildStepDescriptor.filter(Publisher.all(), project.getClass());
727 }
728
729 public static List<SCMDescriptor<?>> getSCMDescriptors(AbstractProject<?,?> project) {
730 return SCM._for(project);
731 }
732
733 public static List<Descriptor<ComputerLauncher>> getComputerLauncherDescriptors() {
734 return Jenkins.getInstance().<ComputerLauncher,Descriptor<ComputerLauncher>>getDescriptorList(ComputerLauncher.class);
735 }
736
737 public static List<Descriptor<RetentionStrategy<?>>> getRetentionStrategyDescriptors() {
738 return RetentionStrategy.all();
739 }
740
741 public static List<ParameterDescriptor> getParameterDescriptors() {
742 return ParameterDefinition.all();
743 }
744
745 public static List<Descriptor<CaptchaSupport>> getCaptchaSupportDescriptors() {
746 return CaptchaSupport.all();
747 }
748
749 public static List<Descriptor<ViewsTabBar>> getViewsTabBarDescriptors() {
750 return ViewsTabBar.all();
751 }
752
753 public static List<Descriptor<MyViewsTabBar>> getMyViewsTabBarDescriptors() {
754 return MyViewsTabBar.all();
755 }
756
757 public static List<NodePropertyDescriptor> getNodePropertyDescriptors(Class<? extends Node> clazz) {
758 List<NodePropertyDescriptor> result = new ArrayList<NodePropertyDescriptor>();
759 Collection<NodePropertyDescriptor> list = (Collection) Jenkins.getInstance().getDescriptorList(NodeProperty.class);
760 for (NodePropertyDescriptor npd : list) {
761 if (npd.isApplicable(clazz)) {
762 result.add(npd);
763 }
764 }
765 return result;
766 }
767
768 /**
769 * Gets all the descriptors sorted by their inheritance tree of {@link Describable}
770 * so that descriptors of similar types come nearby.
771 *
772 * <p>
773 * We sort them by {@link Extension#ordinal()} but only for {@link GlobalConfiguration}s,
774 * as the value is normally used to compare similar kinds of extensions, and we needed
775 * {@link GlobalConfiguration}s to be able to position themselves in a layer above.
776 * This however creates some asymmetry between regular {@link Descriptor}s and {@link GlobalConfiguration}s.
777 * Perhaps it is better to introduce another annotation element? But then,
778 * extensions shouldn't normally concern themselves about ordering too much, and the only reason
779 * we needed this for {@link GlobalConfiguration}s are for backward compatibility.
780 *
781 * @param predicate
782 * Filter the descriptors based on {@link GlobalConfigurationCategory}
783 */
784 public static Collection<Descriptor> getSortedDescriptorsForGlobalConfig(Predicate<GlobalConfigurationCategory> predicate) {
785 ExtensionList<Descriptor> exts = Jenkins.getInstance().getExtensionList(Descriptor.class);
786 List<Tag> r = new ArrayList<Tag>(exts.size());
787
788 for (ExtensionComponent<Descriptor> c : exts.getComponents()) {
789 Descriptor d = c.getInstance();
790 if (d.getGlobalConfigPage()==null) continue;
791
792 if (d instanceof GlobalConfiguration) {
793 if (predicate.apply(((GlobalConfiguration)d).getCategory()))
794 r.add(new Tag(c.ordinal(), d));
795 } else {
796 if (predicate.apply(GlobalConfigurationCategory.get(Unclassified.class)))
797 r.add(new Tag(0, d));
798 }
799 }
800 Collections.sort(r);
801
802 List<Descriptor> answer = new ArrayList<Descriptor>(r.size());
803 for (Tag d : r) answer.add(d.d);
804
805 return DescriptorVisibilityFilter.apply(Jenkins.getInstance(),answer);
806 }
807
808 public static Collection<Descriptor> getSortedDescriptorsForGlobalConfig() {
809 return getSortedDescriptorsForGlobalConfig(Predicates.<GlobalConfigurationCategory>alwaysTrue());
810 }
811
812 public static Collection<Descriptor> getSortedDescriptorsForGlobalConfigNoSecurity() {
813 return getSortedDescriptorsForGlobalConfig(Predicates.not(GlobalSecurityConfiguration.FILTER));
814 }
815
816 private static class Tag implements Comparable<Tag> {
817 double ordinal;
818 String hierarchy;
819 Descriptor d;
820
821 Tag(double ordinal, Descriptor d) {
822 this.ordinal = ordinal;
823 this.d = d;
824 this.hierarchy = buildSuperclassHierarchy(d.clazz, new StringBuilder()).toString();
825 }
826
827 private StringBuilder buildSuperclassHierarchy(Class c, StringBuilder buf) {
828 Class sc = c.getSuperclass();
829 if (sc!=null) buildSuperclassHierarchy(sc,buf).append(':');
830 return buf.append(c.getName());
831 }
832
833 public int compareTo(Tag that) {
834 int r = Double.compare(this.ordinal, that.ordinal);
835 if (r!=0) return -r; // descending for ordinal
836 return this.hierarchy.compareTo(that.hierarchy);
837 }
838 }
839 /**
840 * Computes the path to the icon of the given action
841 * from the context path.
842 */
843 public static String getIconFilePath(Action a) {
844 String name = a.getIconFileName();
845 if (name==null) return null;
846 if (name.startsWith("/"))
847 return name.substring(1);
848 else
849 return "images/24x24/"+name;
850 }
851
852 /**
853 * Works like JSTL build-in size(x) function,
854 * but handle null gracefully.
855 */
856 public static int size2(Object o) throws Exception {
857 if(o==null) return 0;
858 return ASTSizeFunction.sizeOf(o,Introspector.getUberspect());
859 }
860
861 /**
862 * Computes the relative path from the current page to the given item.
863 */
864 public static String getRelativeLinkTo(Item p) {
865 Map<Object,String> ancestors = new HashMap<Object,String>();
866 View view=null;
867
868 StaplerRequest request = Stapler.getCurrentRequest();
869 for( Ancestor a : request.getAncestors() ) {
870 ancestors.put(a.getObject(),a.getRelativePath());
871 if(a.getObject() instanceof View)
872 view = (View) a.getObject();
873 }
874
875 String path = ancestors.get(p);
876 if(path!=null) return path;
877
878 Item i=p;
879 String url = "";
880 while(true) {
881 ItemGroup ig = i.getParent();
882 url = i.getShortUrl()+url;
883
884 if(ig== Jenkins.getInstance()) {
885 assert i instanceof TopLevelItem;
886 if(view!=null && view.contains((TopLevelItem)i)) {
887 // if p and the current page belongs to the same view, then return a relative path
888 return ancestors.get(view)+'/'+url;
889 } else {
890 // otherwise return a path from the root Hudson
891 return request.getContextPath()+'/'+p.getUrl();
892 }
893 }
894
895 path = ancestors.get(ig);
896 if(path!=null) return path+'/'+url;
897
898 assert ig instanceof Item; // if not, ig must have been the Hudson instance
899 i = (Item) ig;
900 }
901 }
902
903 public static Map<Thread,StackTraceElement[]> dumpAllThreads() {
904 Map<Thread,StackTraceElement[]> sorted = new TreeMap<Thread,StackTraceElement[]>(new ThreadSorter());
905 sorted.putAll(Thread.getAllStackTraces());
906 return sorted;
907 }
908
909 @IgnoreJRERequirement
910 public static ThreadInfo[] getThreadInfos() {
911 ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
912 return mbean.dumpAllThreads(mbean.isObjectMonitorUsageSupported(),mbean.isSynchronizerUsageSupported());
913 }
914
915 public static ThreadGroupMap sortThreadsAndGetGroupMap(ThreadInfo[] list) {
916 ThreadGroupMap sorter = new ThreadGroupMap();
917 Arrays.sort(list, sorter);
918 return sorter;
919 }
920
921 // Common code for sorting Threads/ThreadInfos by ThreadGroup
922 private static class ThreadSorterBase {
923 protected Map<Long,String> map = new HashMap<Long,String>();
924
925 private ThreadSorterBase() {
926 ThreadGroup tg = Thread.currentThread().getThreadGroup();
927 while (tg.getParent() != null) tg = tg.getParent();
928 Thread[] threads = new Thread[tg.activeCount()*2];
929 int threadsLen = tg.enumerate(threads, true);
930 for (int i = 0; i < threadsLen; i++)
931 map.put(threads[i].getId(), threads[i].getThreadGroup().getName());
932 }
933
934 protected int compare(long idA, long idB) {
935 String tga = map.get(idA), tgb = map.get(idB);
936 int result = (tga!=null?-1:0) + (tgb!=null?1:0); // Will be non-zero if only one is null
937 if (result==0 && tga!=null)
938 result = tga.compareToIgnoreCase(tgb);
939 return result;
940 }
941 }
942
943 public static class ThreadGroupMap extends ThreadSorterBase implements Comparator<ThreadInfo> {
944
945 /**
946 * @return ThreadGroup name or null if unknown
947 */
948 public String getThreadGroup(ThreadInfo ti) {
949 return map.get(ti.getThreadId());
950 }
951
952 public int compare(ThreadInfo a, ThreadInfo b) {
953 int result = compare(a.getThreadId(), b.getThreadId());
954 if (result == 0)
955 result = a.getThreadName().compareToIgnoreCase(b.getThreadName());
956 return result;
957 }
958 }
959
960 private static class ThreadSorter extends ThreadSorterBase implements Comparator<Thread> {
961
962 public int compare(Thread a, Thread b) {
963 int result = compare(a.getId(), b.getId());
964 if (result == 0)
965 result = a.getName().compareToIgnoreCase(b.getName());
966 return result;
967 }
968 }
969
970 /**
971 * Are we running on JRE6 or above?
972 */
973 @IgnoreJRERequirement
974 public static boolean isMustangOrAbove() {
975 try {
976 System.console();
977 return true;
978 } catch(LinkageError e) {
979 return false;
980 }
981 }
982
983 // ThreadInfo.toString() truncates the stack trace by first 8, so needed my own version
984 @IgnoreJRERequirement
985 public static String dumpThreadInfo(ThreadInfo ti, ThreadGroupMap map) {
986 String grp = map.getThreadGroup(ti);
987 StringBuilder sb = new StringBuilder("\"" + ti.getThreadName() + "\"" +
988 " Id=" + ti.getThreadId() + " Group=" +
989 (grp != null ? grp : "?") + " " +
990 ti.getThreadState());
991 if (ti.getLockName() != null) {
992 sb.append(" on " + ti.getLockName());
993 }
994 if (ti.getLockOwnerName() != null) {
995 sb.append(" owned by \"" + ti.getLockOwnerName() +
996 "\" Id=" + ti.getLockOwnerId());
997 }
998 if (ti.isSuspended()) {
999 sb.append(" (suspended)");
1000 }
1001 if (ti.isInNative()) {
1002 sb.append(" (in native)");
1003 }
1004 sb.append('\n');
1005 StackTraceElement[] stackTrace = ti.getStackTrace();
1006 for (int i=0; i < stackTrace.length; i++) {
1007 StackTraceElement ste = stackTrace[i];
1008 sb.append("\tat " + ste.toString());
1009 sb.append('\n');
1010 if (i == 0 && ti.getLockInfo() != null) {
1011 Thread.State ts = ti.getThreadState();
1012 switch (ts) {
1013 case BLOCKED:
1014 sb.append("\t- blocked on " + ti.getLockInfo());
1015 sb.append('\n');
1016 break;
1017 case WAITING:
1018 sb.append("\t- waiting on " + ti.getLockInfo());
1019 sb.append('\n');
1020 break;
1021 case TIMED_WAITING:
1022 sb.append("\t- waiting on " + ti.getLockInfo());
1023 sb.append('\n');
1024 break;
1025 default:
1026 }
1027 }
1028
1029 for (MonitorInfo mi : ti.getLockedMonitors()) {
1030 if (mi.getLockedStackDepth() == i) {
1031 sb.append("\t- locked " + mi);
1032 sb.append('\n');
1033 }
1034 }
1035 }
1036
1037 LockInfo[] locks = ti.getLockedSynchronizers();
1038 if (locks.length > 0) {
1039 sb.append("\n\tNumber of locked synchronizers = " + locks.length);
1040 sb.append('\n');
1041 for (LockInfo li : locks) {
1042 sb.append("\t- " + li);
1043 sb.append('\n');
1044 }
1045 }
1046 sb.append('\n');
1047 return sb.toString();
1048 }
1049
1050 public static <T> Collection<T> emptyList() {
1051 return Collections.emptyList();
1052 }
1053
1054 public static String jsStringEscape(String s) {
1055 StringBuilder buf = new StringBuilder();
1056 for( int i=0; i<s.length(); i++ ) {
1057 char ch = s.charAt(i);
1058 switch(ch) {
1059 case '\'':
1060 buf.append("\\'");
1061 break;
1062 case '\\':
1063 buf.append("\\\\");
1064 break;
1065 case '"':
1066 buf.append("\\\"");
1067 break;
1068 default:
1069 buf.append(ch);
1070 }
1071 }
1072 return buf.toString();
1073 }
1074
1075 /**
1076 * Converts "abc" to "Abc".
1077 */
1078 public static String capitalize(String s) {
1079 if(s==null || s.length()==0) return s;
1080 return Character.toUpperCase(s.charAt(0))+s.substring(1);
1081 }
1082
1083 public static String getVersion() {
1084 return Jenkins.VERSION;
1085 }
1086
1087 /**
1088 * Resoruce path prefix.
1089 */
1090 public static String getResourcePath() {
1091 return Jenkins.RESOURCE_PATH;
1092 }
1093
1094 public static String getViewResource(Object it, String path) {
1095 Class clazz = it.getClass();
1096
1097 if(it instanceof Class)
1098 clazz = (Class)it;
1099 if(it instanceof Descriptor)
1100 clazz = ((Descriptor)it).clazz;
1101
1102 StringBuilder buf = new StringBuilder(Stapler.getCurrentRequest().getContextPath());
1103 buf.append(Jenkins.VIEW_RESOURCE_PATH).append('/');
1104 buf.append(clazz.getName().replace('.','/').replace('$','/'));
1105 buf.append('/').append(path);
1106
1107 return buf.toString();
1108 }
1109
1110 public static boolean hasView(Object it, String path) throws IOException {
1111 if(it==null) return false;
1112 return Stapler.getCurrentRequest().getView(it,path)!=null;
1113 }
1114
1115 /**
1116 * Can be used to check a checkbox by default.
1117 * Used from views like {@code h.defaultToTrue(scm.useUpdate)}.
1118 * The expression will evaluate to true if scm is null.
1119 */
1120 public static boolean defaultToTrue(Boolean b) {
1121 if(b==null) return true;
1122 return b;
1123 }
1124
1125 /**
1126 * If the value exists, return that value. Otherwise return the default value.
1127 * <p>
1128 * Starting 1.294, JEXL supports the elvis operator "x?:y" that supercedes this.
1129 *
1130 * @since 1.150
1131 */
1132 public static <T> T defaulted(T value, T defaultValue) {
1133 return value!=null ? value : defaultValue;
1134 }
1135
1136 public static String printThrowable(Throwable t) {
1137 StringWriter sw = new StringWriter();
1138 t.printStackTrace(new PrintWriter(sw));
1139 return sw.toString();
1140 }
1141
1142 /**
1143 * Counts the number of rows needed for textarea to fit the content.
1144 * Minimum 5 rows.
1145 */
1146 public static int determineRows(String s) {
1147 if(s==null) return 5;
1148 return Math.max(5,LINE_END.split(s).length);
1149 }
1150
1151 /**
1152 * Converts the Hudson build status to CruiseControl build status,
1153 * which is either Success, Failure, Exception, or Unknown.
1154 */
1155 public static String toCCStatus(Item i) {
1156 if (i instanceof Job) {
1157 Job j = (Job) i;
1158 switch (j.getIconColor().noAnime()) {
1159 case ABORTED:
1160 case RED:
1161 case YELLOW:
1162 return "Failure";
1163 case BLUE:
1164 return "Success";
1165 case DISABLED:
1166 case GREY:
1167 return "Unknown";
1168 }
1169 }
1170 return "Unknown";
1171 }
1172
1173 private static final Pattern LINE_END = Pattern.compile("\r?\n");
1174
1175 /**
1176 * Checks if the current user is anonymous.
1177 */
1178 public static boolean isAnonymous() {
1179 return Jenkins.getAuthentication() instanceof AnonymousAuthenticationToken;
1180 }
1181
1182 /**
1183 * When called from within JEXL expression evaluation,
1184 * this method returns the current {@link JellyContext} used
1185 * to evaluate the script.
1186 *
1187 * @since 1.164
1188 */
1189 public static JellyContext getCurrentJellyContext() {
1190 JellyContext context = ExpressionFactory2.CURRENT_CONTEXT.get();
1191 assert context!=null;
1192 return context;
1193 }
1194
1195 /**
1196 * Evaluate a Jelly script and return output as a String.
1197 *
1198 * @since 1.267
1199 */
1200 public static String runScript(Script script) throws JellyTagException {
1201 StringWriter out = new StringWriter();
1202 script.run(getCurrentJellyContext(), XMLOutput.createXMLOutput(out));
1203 return out.toString();
1204 }
1205
1206 /**
1207 * Returns a sub-list if the given list is bigger than the specified 'maxSize'
1208 */
1209 public static <T> List<T> subList(List<T> base, int maxSize) {
1210 if(maxSize<base.size())
1211 return base.subList(0,maxSize);
1212 else
1213 return base;
1214 }
1215
1216 /**
1217 * Combine path components via '/' while handling leading/trailing '/' to avoid duplicates.
1218 */
1219 public static String joinPath(String... components) {
1220 StringBuilder buf = new StringBuilder();
1221 for (String s : components) {
1222 if (s.length()==0) continue;
1223
1224 if (buf.length()>0) {
1225 if (buf.charAt(buf.length()-1)!='/')
1226 buf.append('/');
1227 if (s.charAt(0)=='/') s=s.substring(1);
1228 }
1229 buf.append(s);
1230 }
1231 return buf.toString();
1232 }
1233
1234 /**
1235 * Computes the hyperlink to actions, to handle the situation when the {@link Action#getUrlName()}
1236 * returns absolute URL.
1237 */
1238 public static String getActionUrl(String itUrl,Action action) {
1239 String urlName = action.getUrlName();
1240 if(urlName==null) return null; // to avoid NPE and fail to render the whole page
1241 try {
1242 if (new URI(urlName).isAbsolute()) {
1243 return urlName;
1244 }
1245 } catch (URISyntaxException x) {
1246 Logger.getLogger(Functions.class.getName()).log(Level.WARNING, "Failed to parse URL for {0}: {1}", new Object[] {action, x});
1247 return null;
1248 }
1249 if(urlName.startsWith("/"))
1250 return joinPath(Stapler.getCurrentRequest().getContextPath(),urlName);
1251 else
1252 // relative URL name
1253 return joinPath(Stapler.getCurrentRequest().getContextPath()+'/'+itUrl,urlName);
1254 }
1255
1256 /**
1257 * Escapes the character unsafe for e-mail address.
1258 * See http://en.wikipedia.org/wiki/E-mail_address for the details,
1259 * but here the vocabulary is even more restricted.
1260 */
1261 public static String toEmailSafeString(String projectName) {
1262 // TODO: escape non-ASCII characters
1263 StringBuilder buf = new StringBuilder(projectName.length());
1264 for( int i=0; i<projectName.length(); i++ ) {
1265 char ch = projectName.charAt(i);
1266 if(('a'<=ch && ch<='z')
1267 || ('z'<=ch && ch<='Z')
1268 || ('0'<=ch && ch<='9')
1269 || "-_.".indexOf(ch)>=0)
1270 buf.append(ch);
1271 else
1272 buf.append('_'); // escape
1273 }
1274 return projectName;
1275 }
1276
1277 public String getSystemProperty(String key) {
1278 return System.getProperty(key);
1279 }
1280
1281 /**
1282 * Obtains the host name of the Hudson server that clients can use to talk back to.
1283 * <p>
1284 * This is primarily used in <tt>slave-agent.jnlp.jelly</tt> to specify the destination
1285 * that the slaves talk to.
1286 */
1287 public String getServerName() {
1288 // Try to infer this from the configured root URL.
1289 // This makes it work correctly when Hudson runs behind a reverse proxy.
1290 String url = Jenkins.getInstance().getRootUrl();
1291 try {
1292 if(url!=null) {
1293 String host = new URL(url).getHost();
1294 if(host!=null)
1295 return host;
1296 }
1297 } catch (MalformedURLException e) {
1298 // fall back to HTTP request
1299 }
1300 return Stapler.getCurrentRequest().getServerName();
1301 }
1302
1303 /**
1304 * Determines the form validation check URL. See textbox.jelly
1305 */
1306 public String getCheckUrl(String userDefined, Object descriptor, String field) {
1307 if(userDefined!=null || field==null) return userDefined;
1308 if (descriptor instanceof Descriptor) {
1309 Descriptor d = (Descriptor) descriptor;
1310 return d.getCheckUrl(field);
1311 }
1312 return null;
1313 }
1314
1315 /**
1316 * If the given href link is matching the current page, return true.
1317 *
1318 * Used in <tt>task.jelly</tt> to decide if the page should be highlighted.
1319 */
1320 public boolean hyperlinkMatchesCurrentPage(String href) throws UnsupportedEncodingException {
1321 String url = Stapler.getCurrentRequest().getRequestURL().toString();
1322 if (href == null || href.length() <= 1) return ".".equals(href) && url.endsWith("/");
1323 url = URLDecoder.decode(url,"UTF-8");
1324 href = URLDecoder.decode(href,"UTF-8");
1325 if (url.endsWith("/")) url = url.substring(0, url.length() - 1);
1326 if (href.endsWith("/")) href = href.substring(0, href.length() - 1);
1327
1328 return url.endsWith(href);
1329 }
1330
1331 public <T> List<T> singletonList(T t) {
1332 return Collections.singletonList(t);
1333 }
1334
1335 /**
1336 * Gets all the {@link PageDecorator}s.
1337 */
1338 public static List<PageDecorator> getPageDecorators() {
1339 // this method may be called to render start up errors, at which point Hudson doesn't exist yet. see HUDSON-3608
1340 if(Jenkins.getInstance()==null) return Collections.emptyList();
1341 return PageDecorator.all();
1342 }
1343
1344 public static List<Descriptor<Cloud>> getCloudDescriptors() {
1345 return Cloud.all();
1346 }
1347
1348 /**
1349 * Prepend a prefix only when there's the specified body.
1350 */
1351 public String prepend(String prefix, String body) {
1352 if(body!=null && body.length()>0)
1353 return prefix+body;
1354 return body;
1355 }
1356
1357 public static List<Descriptor<CrumbIssuer>> getCrumbIssuerDescriptors() {
1358 return CrumbIssuer.all();
1359 }
1360
1361 public static String getCrumb(StaplerRequest req) {
1362 Jenkins h = Jenkins.getInstance();
1363 CrumbIssuer issuer = h != null ? h.getCrumbIssuer() : null;
1364 return issuer != null ? issuer.getCrumb(req) : "";
1365 }
1366
1367 public static String getCrumbRequestField() {
1368 Jenkins h = Jenkins.getInstance();
1369 CrumbIssuer issuer = h != null ? h.getCrumbIssuer() : null;
1370 return issuer != null ? issuer.getDescriptor().getCrumbRequestField() : "";
1371 }
1372
1373 public static Date getCurrentTime() {
1374 return new Date();
1375 }
1376
1377 public static Locale getCurrentLocale() {
1378 Locale locale=null;
1379 StaplerRequest req = Stapler.getCurrentRequest();
1380 if(req!=null)
1381 locale = req.getLocale();
1382 if(locale==null)
1383 locale = Locale.getDefault();
1384 return locale;
1385 }
1386
1387 /**
1388 * Generate a series of &lt;script> tags to include <tt>script.js</tt>
1389 * from {@link ConsoleAnnotatorFactory}s and {@link ConsoleAnnotationDescriptor}s.
1390 */
1391 public static String generateConsoleAnnotationScriptAndStylesheet() {
1392 String cp = Stapler.getCurrentRequest().getContextPath();
1393 StringBuilder buf = new StringBuilder();
1394 for (ConsoleAnnotatorFactory f : ConsoleAnnotatorFactory.all()) {
1395 String path = cp + "/extensionList/" + ConsoleAnnotatorFactory.class.getName() + "/" + f.getClass().getName();
1396 if (f.hasScript())
1397 buf.append("<script src='"+path+"/script.js'></script>");
1398 if (f.hasStylesheet())
1399 buf.append("<link rel='stylesheet' type='text/css' href='"+path+"/style.css' />");
1400 }
1401 for (ConsoleAnnotationDescriptor d : ConsoleAnnotationDescriptor.all()) {
1402 String path = cp+"/descriptor/"+d.clazz.getName();
1403 if (d.hasScript())
1404 buf.append("<script src='"+path+"/script.js'></script>");
1405 if (d.hasStylesheet())
1406 buf.append("<link rel='stylesheet' type='text/css' href='"+path+"/style.css' />");
1407 }
1408 return buf.toString();
1409 }
1410
1411 /**
1412 * Work around for bug 6935026.
1413 */
1414 public List<String> getLoggerNames() {
1415 while (true) {
1416 try {
1417 List<String> r = new ArrayList<String>();
1418 Enumeration<String> e = LogManager.getLogManager().getLoggerNames();
1419 while (e.hasMoreElements())
1420 r.add(e.nextElement());
1421 return r;
1422 } catch (ConcurrentModificationException e) {
1423 // retry
1424 }
1425 }
1426 }
1427
1428 /**
1429 * Used by &lt;f:password/> so that we send an encrypted value to the client.
1430 */
1431 public String getPasswordValue(Object o) {
1432 if (o==null) return null;
1433 if (o instanceof Secret) return ((Secret)o).getEncryptedValue();
1434 return o.toString();
1435 }
1436
1437 public List filterDescriptors(Object context, Iterable descriptors) {
1438 return DescriptorVisibilityFilter.apply(context,descriptors);
1439 }
1440
1441 /**
1442 * Returns true if we are running unit tests.
1443 */
1444 public static boolean getIsUnitTest() {
1445 return Main.isUnitTest;
1446 }
1447
1448 /**
1449 * Returns {@code true} if the {@link Run#ARTIFACTS} permission is enabled,
1450 * {@code false} otherwise.
1451 *
1452 * <p>When the {@link Run#ARTIFACTS} permission is not turned on using the
1453 * {@code hudson.security.ArtifactsPermission} system property, this
1454 * permission must not be considered to be set to {@code false} for every
1455 * user. It must rather be like if the permission doesn't exist at all
1456 * (which means that every user has to have an access to the artifacts but
1457 * the permission can't be configured in the security screen). Got it?</p>
1458 */
1459 public static boolean isArtifactsPermissionEnabled() {
1460 return Boolean.getBoolean("hudson.security.ArtifactsPermission");
1461 }
1462
1463 /**
1464 * Returns {@code true} if the {@link Item#WIPEOUT} permission is enabled,
1465 * {@code false} otherwise.
1466 *
1467 * <p>The "Wipe Out Workspace" action available on jobs is controlled by the
1468 * {@link Item#BUILD} permission. For some specific projects, however, it is
1469 * not acceptable to let users have this possibility, even it they can
1470 * trigger builds. As such, when enabling the {@code hudson.security.WipeOutPermission}
1471 * system property, a new "WipeOut" permission will allow to have greater
1472 * control on the "Wipe Out Workspace" action.</p>
1473 */
1474 public static boolean isWipeOutPermissionEnabled() {
1475 return Boolean.getBoolean("hudson.security.WipeOutPermission");
1476 }
1477
1478 public static String createRenderOnDemandProxy(JellyContext context, String attributesToCapture) {
1479 return Stapler.getCurrentRequest().createJavaScriptProxy(new RenderOnDemandClosure(context,attributesToCapture));
1480 }
1481
1482 public static String getCurrentDescriptorByNameUrl() {
1483 return Descriptor.getCurrentDescriptorByNameUrl();
1484 }
1485
1486 public static String setCurrentDescriptorByNameUrl(String value) {
1487 String o = getCurrentDescriptorByNameUrl();
1488 Stapler.getCurrentRequest().setAttribute("currentDescriptorByNameUrl", value);
1489
1490 return o;
1491 }
1492
1493 public static void restoreCurrentDescriptorByNameUrl(String old) {
1494 Stapler.getCurrentRequest().setAttribute("currentDescriptorByNameUrl", old);
1495 }
1496
1497 public static List<String> getRequestHeaders(String name) {
1498 List<String> r = new ArrayList<String>();
1499 Enumeration e = Stapler.getCurrentRequest().getHeaders(name);
1500 while (e.hasMoreElements()) {
1501 r.add(e.nextElement().toString());
1502 }
1503 return r;
1504 }
1505
1506 /**
1507 * Used for arguments to internationalized expressions to avoid escape
1508 */
1509 public static Object rawHtml(Object o) {
1510 return o==null ? null : new RawHtmlArgument(o);
1511 }
1512
1513 public static ArrayList<CLICommand> getCLICommands() {
1514 ArrayList<CLICommand> all = new ArrayList<CLICommand>(CLICommand.all());
1515 Collections.sort(all, new Comparator<CLICommand>() {
1516 public int compare(CLICommand cliCommand, CLICommand cliCommand1) {
1517 return cliCommand.getName().compareTo(cliCommand1.getName());
1518 }
1519 });
1520 return all;
1521 }
1522
1523 /**
1524 * Returns an avatar image URL for the specified user and preferred image size
1525 * @param user the user
1526 * @param avatarSize the preferred size of the avatar image
1527 * @return a URL string
1528 * @since 1.433
1529 */
1530 public static String getAvatar(User user, String avatarSize) {
1531 return UserAvatarResolver.resolve(user, avatarSize);
1532 }
1533
1534 /**
1535 * @deprecated as of 1.451
1536 * Use {@link #getAvatar}
1537 */
1538 public String getUserAvatar(User user, String avatarSize) {
1539 return getAvatar(user,avatarSize);
1540 }
1541
1542
1543 /**
1544 * Returns human readable information about file size
1545 *
1546 * @param file size in bytes
1547 * @return file size in appropriate unit
1548 */
1549 public static String humanReadableByteSize(long size){
1550 String measure = "B";
1551 if(size < 1024){
1552 return size + " " + measure;
1553 }
1554 Double number = new Double(size);
1555 if(number>=1024){
1556 number = number/1024;
1557 measure = "KB";
1558 if(number>=1024){
1559 number = number/1024;
1560 measure = "MB";
1561 if(number>=1024){
1562 number=number/1024;
1563 measure = "GB";
1564 }
1565 }
1566 }
1567 DecimalFormat format = new DecimalFormat("#0.00");
1568 return format.format(number) + " " + measure;
1569 }
1570 }
1571