~ubuntu-branches/ubuntu/saucy/jenkins/saucy-proposed

« back to all changes in this revision

Viewing changes to .pc/build/remove-findbugs.patch/core/src/main/java/jenkins/model/Jenkins.java

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-08-13 12:35:19 UTC
  • mfrom: (1.1.13)
  • Revision ID: package-import@ubuntu.com-20130813123519-tizgfxcr70trl7r0
Tags: 1.509.2+dfsg-1
* New upstream release (Closes: #706725):
  - d/control: Update versioned BD's:
    * jenkins-executable-war >= 1.28.
    * jenkins-instance-identity >= 1.3.
    * libjenkins-remoting-java >= 2.23.
    * libjenkins-winstone-java >= 0.9.10-jenkins-44.
    * libstapler-java >= 1.207.
    * libjenkins-json-java >= 2.4-jenkins-1.
    * libstapler-adjunct-timeline-java >= 1.4.
    * libstapler-adjunct-codemirror-java >= 1.2.
    * libmaven-hpi-plugin-java >= 1.93.
    * libjenkins-xstream-java >= 1.4.4-jenkins-3.
  - d/maven.rules: Map to older version of animal-sniffer-maven-plugin.
  - Add patch for compatibility with guava >= 0.14.
  - Add patch to exclude asm4 dependency via jnr-posix.
  - Fixes the following security vulnerabilities:
    CVE-2013-2034, CVE-2013-2033, CVE-2013-2034, CVE-2013-1808
* d/patches/*: Switch to using git patch-queue for managing patches.
* De-duplicate jars between libjenkins-java and jenkins-external-job-monitor
  (Closes: #701163):
  - d/control: Add dependency between jenkins-external-job-monitor ->
    libjenkins-java.
  - d/rules: 
    Drop installation of jenkins-core in jenkins-external-job-monitor.
  - d/jenkins-external-job-monitor.{links,install}: Link to jenkins-core
    in /usr/share/java instead of included version.
* Wait longer for jenkins to stop during restarts (Closes: #704848):
  - d/jenkins.init: Re-sync init script from upstream codebase.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * The MIT License
3
 
 *
4
 
 * Copyright (c) 2004-2011, Sun Microsystems, Inc., Kohsuke Kawaguchi,
5
 
 * Erik Ramfelt, Koichi Fujikawa, Red Hat, Inc., Seiji Sogabe,
6
 
 * Stephen Connolly, Tom Huybrechts, Yahoo! Inc., Alan Harder, CloudBees, Inc.,
7
 
 * Yahoo!, Inc.
8
 
 *
9
 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
10
 
 * of this software and associated documentation files (the "Software"), to deal
11
 
 * in the Software without restriction, including without limitation the rights
12
 
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
 
 * copies of the Software, and to permit persons to whom the Software is
14
 
 * furnished to do so, subject to the following conditions:
15
 
 *
16
 
 * The above copyright notice and this permission notice shall be included in
17
 
 * all copies or substantial portions of the Software.
18
 
 *
19
 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
 
 * THE SOFTWARE.
26
 
 */
27
 
package jenkins.model;
28
 
 
29
 
import com.google.common.collect.Lists;
30
 
import com.google.inject.Injector;
31
 
import hudson.ExtensionComponent;
32
 
import hudson.ExtensionFinder;
33
 
import hudson.model.LoadStatistics;
34
 
import hudson.model.Messages;
35
 
import hudson.model.Node;
36
 
import hudson.model.AbstractCIBase;
37
 
import hudson.model.AbstractProject;
38
 
import hudson.model.Action;
39
 
import hudson.model.AdministrativeMonitor;
40
 
import hudson.model.AllView;
41
 
import hudson.model.Api;
42
 
import hudson.model.Computer;
43
 
import hudson.model.ComputerSet;
44
 
import hudson.model.DependencyGraph;
45
 
import hudson.model.Describable;
46
 
import hudson.model.Descriptor;
47
 
import hudson.model.DescriptorByNameOwner;
48
 
import hudson.model.DirectoryBrowserSupport;
49
 
import hudson.model.Failure;
50
 
import hudson.model.Fingerprint;
51
 
import hudson.model.FingerprintCleanupThread;
52
 
import hudson.model.FingerprintMap;
53
 
import hudson.model.FullDuplexHttpChannel;
54
 
import hudson.model.Hudson;
55
 
import hudson.model.Item;
56
 
import hudson.model.ItemGroup;
57
 
import hudson.model.ItemGroupMixIn;
58
 
import hudson.model.Items;
59
 
import hudson.model.JDK;
60
 
import hudson.model.Job;
61
 
import hudson.model.JobPropertyDescriptor;
62
 
import hudson.model.Label;
63
 
import hudson.model.ListView;
64
 
import hudson.model.LoadBalancer;
65
 
import hudson.model.ManagementLink;
66
 
import hudson.model.NoFingerprintMatch;
67
 
import hudson.model.OverallLoadStatistics;
68
 
import hudson.model.Project;
69
 
import hudson.model.RestartListener;
70
 
import hudson.model.RootAction;
71
 
import hudson.model.Slave;
72
 
import hudson.model.TaskListener;
73
 
import hudson.model.TopLevelItem;
74
 
import hudson.model.TopLevelItemDescriptor;
75
 
import hudson.model.UnprotectedRootAction;
76
 
import hudson.model.UpdateCenter;
77
 
import hudson.model.User;
78
 
import hudson.model.View;
79
 
import hudson.model.ViewGroup;
80
 
import hudson.model.ViewGroupMixIn;
81
 
import hudson.model.Descriptor.FormException;
82
 
import hudson.model.labels.LabelAtom;
83
 
import hudson.model.listeners.ItemListener;
84
 
import hudson.model.listeners.SCMListener;
85
 
import hudson.model.listeners.SaveableListener;
86
 
import hudson.model.Queue;
87
 
import hudson.model.WorkspaceCleanupThread;
88
 
 
89
 
import antlr.ANTLRException;
90
 
import com.google.common.collect.ImmutableMap;
91
 
import com.thoughtworks.xstream.XStream;
92
 
import hudson.BulkChange;
93
 
import hudson.DNSMultiCast;
94
 
import hudson.DescriptorExtensionList;
95
 
import hudson.Extension;
96
 
import hudson.ExtensionList;
97
 
import hudson.ExtensionPoint;
98
 
import hudson.FilePath;
99
 
import hudson.Functions;
100
 
import hudson.Launcher;
101
 
import hudson.Launcher.LocalLauncher;
102
 
import hudson.LocalPluginManager;
103
 
import hudson.Lookup;
104
 
import hudson.markup.MarkupFormatter;
105
 
import hudson.Plugin;
106
 
import hudson.PluginManager;
107
 
import hudson.PluginWrapper;
108
 
import hudson.ProxyConfiguration;
109
 
import hudson.TcpSlaveAgentListener;
110
 
import hudson.UDPBroadcastThread;
111
 
import hudson.Util;
112
 
import static hudson.Util.fixEmpty;
113
 
import static hudson.Util.fixNull;
114
 
import hudson.WebAppMain;
115
 
import hudson.XmlFile;
116
 
import hudson.cli.CLICommand;
117
 
import hudson.cli.CliEntryPoint;
118
 
import hudson.cli.CliManagerImpl;
119
 
import hudson.cli.declarative.CLIMethod;
120
 
import hudson.cli.declarative.CLIResolver;
121
 
import hudson.init.InitMilestone;
122
 
import hudson.init.InitStrategy;
123
 
import hudson.lifecycle.Lifecycle;
124
 
import hudson.logging.LogRecorderManager;
125
 
import hudson.lifecycle.RestartNotSupportedException;
126
 
import hudson.markup.RawHtmlMarkupFormatter;
127
 
import hudson.remoting.Channel;
128
 
import hudson.remoting.LocalChannel;
129
 
import hudson.remoting.VirtualChannel;
130
 
import hudson.scm.RepositoryBrowser;
131
 
import hudson.scm.SCM;
132
 
import hudson.search.CollectionSearchIndex;
133
 
import hudson.search.SearchIndexBuilder;
134
 
import hudson.search.SearchItem;
135
 
import hudson.security.ACL;
136
 
import hudson.security.AccessControlled;
137
 
import hudson.security.AuthorizationStrategy;
138
 
import hudson.security.BasicAuthenticationFilter;
139
 
import hudson.security.FederatedLoginService;
140
 
import hudson.security.FullControlOnceLoggedInAuthorizationStrategy;
141
 
import hudson.security.HudsonFilter;
142
 
import hudson.security.LegacyAuthorizationStrategy;
143
 
import hudson.security.LegacySecurityRealm;
144
 
import hudson.security.Permission;
145
 
import hudson.security.PermissionGroup;
146
 
import hudson.security.PermissionScope;
147
 
import hudson.security.SecurityMode;
148
 
import hudson.security.SecurityRealm;
149
 
import hudson.security.csrf.CrumbIssuer;
150
 
import hudson.slaves.Cloud;
151
 
import hudson.slaves.ComputerListener;
152
 
import hudson.slaves.DumbSlave;
153
 
import hudson.slaves.EphemeralNode;
154
 
import hudson.slaves.NodeDescriptor;
155
 
import hudson.slaves.NodeList;
156
 
import hudson.slaves.NodeProperty;
157
 
import hudson.slaves.NodePropertyDescriptor;
158
 
import hudson.slaves.NodeProvisioner;
159
 
import hudson.slaves.OfflineCause;
160
 
import hudson.slaves.RetentionStrategy;
161
 
import hudson.tasks.BuildWrapper;
162
 
import hudson.tasks.Builder;
163
 
import hudson.tasks.Mailer;
164
 
import hudson.tasks.Publisher;
165
 
import hudson.triggers.SafeTimerTask;
166
 
import hudson.triggers.Trigger;
167
 
import hudson.triggers.TriggerDescriptor;
168
 
import hudson.util.AdministrativeError;
169
 
import hudson.util.CaseInsensitiveComparator;
170
 
import hudson.util.ClockDifference;
171
 
import hudson.util.CopyOnWriteList;
172
 
import hudson.util.CopyOnWriteMap;
173
 
import hudson.util.DaemonThreadFactory;
174
 
import hudson.util.DescribableList;
175
 
import hudson.util.FormApply;
176
 
import hudson.util.FormValidation;
177
 
import hudson.util.Futures;
178
 
import hudson.util.HudsonIsLoading;
179
 
import hudson.util.HudsonIsRestarting;
180
 
import hudson.util.Iterators;
181
 
import hudson.util.JenkinsReloadFailed;
182
 
import hudson.util.Memoizer;
183
 
import hudson.util.MultipartFormDataParser;
184
 
import hudson.util.RemotingDiagnostics;
185
 
import hudson.util.RemotingDiagnostics.HeapDump;
186
 
import hudson.util.StreamTaskListener;
187
 
import hudson.util.TextFile;
188
 
import hudson.util.TimeUnit2;
189
 
import hudson.util.VersionNumber;
190
 
import hudson.util.XStream2;
191
 
import hudson.views.DefaultMyViewsTabBar;
192
 
import hudson.views.DefaultViewsTabBar;
193
 
import hudson.views.MyViewsTabBar;
194
 
import hudson.views.ViewsTabBar;
195
 
import hudson.widgets.Widget;
196
 
import jenkins.ExtensionComponentSet;
197
 
import jenkins.ExtensionRefreshException;
198
 
import jenkins.InitReactorRunner;
199
 
import jenkins.model.ProjectNamingStrategy.DefaultProjectNamingStrategy;
200
 
import jenkins.security.ConfidentialKey;
201
 
import jenkins.security.ConfidentialStore;
202
 
import jenkins.util.io.FileBoolean;
203
 
import net.sf.json.JSONObject;
204
 
import org.acegisecurity.AccessDeniedException;
205
 
import org.acegisecurity.AcegiSecurityException;
206
 
import org.acegisecurity.Authentication;
207
 
import org.acegisecurity.GrantedAuthority;
208
 
import org.acegisecurity.GrantedAuthorityImpl;
209
 
import org.acegisecurity.context.SecurityContextHolder;
210
 
import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
211
 
import org.acegisecurity.ui.AbstractProcessingFilter;
212
 
import org.apache.commons.jelly.JellyException;
213
 
import org.apache.commons.jelly.Script;
214
 
import org.apache.commons.logging.LogFactory;
215
 
import org.jvnet.hudson.reactor.Executable;
216
 
import org.jvnet.hudson.reactor.ReactorException;
217
 
import org.jvnet.hudson.reactor.Task;
218
 
import org.jvnet.hudson.reactor.TaskBuilder;
219
 
import org.jvnet.hudson.reactor.TaskGraphBuilder;
220
 
import org.jvnet.hudson.reactor.Reactor;
221
 
import org.jvnet.hudson.reactor.TaskGraphBuilder.Handle;
222
 
import org.kohsuke.accmod.Restricted;
223
 
import org.kohsuke.accmod.restrictions.NoExternalUse;
224
 
import org.kohsuke.args4j.Argument;
225
 
import org.kohsuke.args4j.Option;
226
 
import org.kohsuke.stapler.Ancestor;
227
 
import org.kohsuke.stapler.HttpRedirect;
228
 
import org.kohsuke.stapler.HttpResponse;
229
 
import org.kohsuke.stapler.HttpResponses;
230
 
import org.kohsuke.stapler.MetaClass;
231
 
import org.kohsuke.stapler.QueryParameter;
232
 
import org.kohsuke.stapler.Stapler;
233
 
import org.kohsuke.stapler.StaplerFallback;
234
 
import org.kohsuke.stapler.StaplerProxy;
235
 
import org.kohsuke.stapler.StaplerRequest;
236
 
import org.kohsuke.stapler.StaplerResponse;
237
 
import org.kohsuke.stapler.WebApp;
238
 
import org.kohsuke.stapler.export.Exported;
239
 
import org.kohsuke.stapler.export.ExportedBean;
240
 
import org.kohsuke.stapler.framework.adjunct.AdjunctManager;
241
 
import org.kohsuke.stapler.interceptor.RequirePOST;
242
 
import org.kohsuke.stapler.jelly.JellyClassLoaderTearOff;
243
 
import org.kohsuke.stapler.jelly.JellyRequestDispatcher;
244
 
import org.xml.sax.InputSource;
245
 
 
246
 
import javax.crypto.SecretKey;
247
 
import javax.servlet.RequestDispatcher;
248
 
import javax.servlet.ServletContext;
249
 
import javax.servlet.ServletException;
250
 
import javax.servlet.http.Cookie;
251
 
import javax.servlet.http.HttpServletResponse;
252
 
 
253
 
import static hudson.init.InitMilestone.*;
254
 
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
255
 
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
256
 
import java.io.File;
257
 
import java.io.FileFilter;
258
 
import java.io.IOException;
259
 
import java.io.InputStream;
260
 
import java.io.PrintWriter;
261
 
import java.io.StringWriter;
262
 
import java.net.BindException;
263
 
import java.net.URL;
264
 
import java.nio.charset.Charset;
265
 
import java.security.SecureRandom;
266
 
import java.text.Collator;
267
 
import java.text.ParseException;
268
 
import java.util.ArrayList;
269
 
import java.util.Arrays;
270
 
import java.util.Collection;
271
 
import java.util.Collections;
272
 
import java.util.Comparator;
273
 
import java.util.HashMap;
274
 
import java.util.HashSet;
275
 
import java.util.Iterator;
276
 
import java.util.List;
277
 
import java.util.Map;
278
 
import java.util.Map.Entry;
279
 
import java.util.Properties;
280
 
import java.util.Set;
281
 
import java.util.Stack;
282
 
import java.util.StringTokenizer;
283
 
import java.util.Timer;
284
 
import java.util.TreeSet;
285
 
import java.util.UUID;
286
 
import java.util.concurrent.ConcurrentHashMap;
287
 
import java.util.concurrent.CopyOnWriteArrayList;
288
 
import java.util.concurrent.ExecutionException;
289
 
import java.util.concurrent.ExecutorService;
290
 
import java.util.concurrent.Future;
291
 
import java.util.concurrent.LinkedBlockingQueue;
292
 
import java.util.concurrent.ThreadPoolExecutor;
293
 
import java.util.concurrent.TimeUnit;
294
 
import java.util.concurrent.TimeoutException;
295
 
import java.util.logging.Level;
296
 
import static java.util.logging.Level.SEVERE;
297
 
import java.util.logging.LogRecord;
298
 
import java.util.logging.Logger;
299
 
import java.util.regex.Pattern;
300
 
import javax.annotation.CheckForNull;
301
 
 
302
 
/**
303
 
 * Root object of the system.
304
 
 *
305
 
 * @author Kohsuke Kawaguchi
306
 
 */
307
 
@ExportedBean
308
 
public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGroup, StaplerProxy, StaplerFallback, ViewGroup, AccessControlled, DescriptorByNameOwner, ModelObjectWithContextMenu {
309
 
    private transient final Queue queue;
310
 
 
311
 
    /**
312
 
     * Stores various objects scoped to {@link Jenkins}.
313
 
     */
314
 
    public transient final Lookup lookup = new Lookup();
315
 
 
316
 
    /**
317
 
     * We update this field to the current version of Hudson whenever we save {@code config.xml}.
318
 
     * This can be used to detect when an upgrade happens from one version to next.
319
 
     *
320
 
     * <p>
321
 
     * Since this field is introduced starting 1.301, "1.0" is used to represent every version
322
 
     * up to 1.300. This value may also include non-standard versions like "1.301-SNAPSHOT" or
323
 
     * "?", etc., so parsing needs to be done with a care.
324
 
     *
325
 
     * @since 1.301
326
 
     */
327
 
    // this field needs to be at the very top so that other components can look at this value even during unmarshalling
328
 
    private String version = "1.0";
329
 
 
330
 
    /**
331
 
     * Number of executors of the master node.
332
 
     */
333
 
    private int numExecutors = 2;
334
 
 
335
 
    /**
336
 
     * Job allocation strategy.
337
 
     */
338
 
    private Mode mode = Mode.NORMAL;
339
 
 
340
 
    /**
341
 
     * False to enable anyone to do anything.
342
 
     * Left as a field so that we can still read old data that uses this flag.
343
 
     *
344
 
     * @see #authorizationStrategy
345
 
     * @see #securityRealm
346
 
     */
347
 
    private Boolean useSecurity;
348
 
 
349
 
    /**
350
 
     * Controls how the
351
 
     * <a href="http://en.wikipedia.org/wiki/Authorization">authorization</a>
352
 
     * is handled in Hudson.
353
 
     * <p>
354
 
     * This ultimately controls who has access to what.
355
 
     *
356
 
     * Never null.
357
 
     */
358
 
    private volatile AuthorizationStrategy authorizationStrategy = AuthorizationStrategy.UNSECURED;
359
 
 
360
 
    /**
361
 
     * Controls a part of the
362
 
     * <a href="http://en.wikipedia.org/wiki/Authentication">authentication</a>
363
 
     * handling in Hudson.
364
 
     * <p>
365
 
     * Intuitively, this corresponds to the user database.
366
 
     *
367
 
     * See {@link HudsonFilter} for the concrete authentication protocol.
368
 
     *
369
 
     * Never null. Always use {@link #setSecurityRealm(SecurityRealm)} to
370
 
     * update this field.
371
 
     *
372
 
     * @see #getSecurity()
373
 
     * @see #setSecurityRealm(SecurityRealm)
374
 
     */
375
 
    private volatile SecurityRealm securityRealm = SecurityRealm.NO_AUTHENTICATION;
376
 
    
377
 
    /**
378
 
     * The project naming strategy defines/restricts the names which can be given to a project/job. e.g. does the name have to follow a naming convention?
379
 
     */
380
 
    private ProjectNamingStrategy projectNamingStrategy = DefaultProjectNamingStrategy.DEFAULT_NAMING_STRATEGY;
381
 
 
382
 
    /**
383
 
     * Root directory for the workspaces. This value will be variable-expanded against
384
 
     * job name and JENKINS_HOME.
385
 
     *
386
 
     * @see #getWorkspaceFor(TopLevelItem)
387
 
     */
388
 
    private String workspaceDir = "${ITEM_ROOTDIR}/"+WORKSPACE_DIRNAME;
389
 
 
390
 
    /**
391
 
     * Root directory for the workspaces. This value will be variable-expanded against
392
 
     * job name and JENKINS_HOME.
393
 
     *
394
 
     * @see #getBuildDirFor(Job)
395
 
     */
396
 
    private String buildsDir = "${ITEM_ROOTDIR}/builds";
397
 
 
398
 
    /**
399
 
     * Message displayed in the top page.
400
 
     */
401
 
    private String systemMessage;
402
 
 
403
 
    private MarkupFormatter markupFormatter;
404
 
 
405
 
    /**
406
 
     * Root directory of the system.
407
 
     */
408
 
    public transient final File root;
409
 
 
410
 
    /**
411
 
     * Where are we in the initialization?
412
 
     */
413
 
    private transient volatile InitMilestone initLevel = InitMilestone.STARTED;
414
 
 
415
 
    /**
416
 
     * All {@link Item}s keyed by their {@link Item#getName() name}s.
417
 
     */
418
 
    /*package*/ transient final Map<String,TopLevelItem> items = new CopyOnWriteMap.Tree<String,TopLevelItem>(CaseInsensitiveComparator.INSTANCE);
419
 
 
420
 
    /**
421
 
     * The sole instance.
422
 
     */
423
 
    private static Jenkins theInstance;
424
 
 
425
 
    private transient volatile boolean isQuietingDown;
426
 
    private transient volatile boolean terminating;
427
 
 
428
 
    private List<JDK> jdks = new ArrayList<JDK>();
429
 
 
430
 
    private transient volatile DependencyGraph dependencyGraph;
431
 
 
432
 
    /**
433
 
     * Currently active Views tab bar.
434
 
     */
435
 
    private volatile ViewsTabBar viewsTabBar = new DefaultViewsTabBar();
436
 
 
437
 
    /**
438
 
     * Currently active My Views tab bar.
439
 
     */
440
 
    private volatile MyViewsTabBar myViewsTabBar = new DefaultMyViewsTabBar();
441
 
 
442
 
    /**
443
 
     * All {@link ExtensionList} keyed by their {@link ExtensionList#extensionType}.
444
 
     */
445
 
    private transient final Memoizer<Class,ExtensionList> extensionLists = new Memoizer<Class,ExtensionList>() {
446
 
        public ExtensionList compute(Class key) {
447
 
            return ExtensionList.create(Jenkins.this,key);
448
 
        }
449
 
    };
450
 
 
451
 
    /**
452
 
     * All {@link DescriptorExtensionList} keyed by their {@link DescriptorExtensionList#describableType}.
453
 
     */
454
 
    private transient final Memoizer<Class,DescriptorExtensionList> descriptorLists = new Memoizer<Class,DescriptorExtensionList>() {
455
 
        public DescriptorExtensionList compute(Class key) {
456
 
            return DescriptorExtensionList.createDescriptorList(Jenkins.this,key);
457
 
        }
458
 
    };
459
 
 
460
 
    /**
461
 
     * {@link Computer}s in this Hudson system. Read-only.
462
 
     */
463
 
    protected transient final Map<Node,Computer> computers = new CopyOnWriteMap.Hash<Node,Computer>();
464
 
 
465
 
    /**
466
 
     * Active {@link Cloud}s.
467
 
     */
468
 
    public final Hudson.CloudList clouds = new Hudson.CloudList(this);
469
 
 
470
 
    public static class CloudList extends DescribableList<Cloud,Descriptor<Cloud>> {
471
 
        public CloudList(Jenkins h) {
472
 
            super(h);
473
 
        }
474
 
 
475
 
        public CloudList() {// needed for XStream deserialization
476
 
        }
477
 
 
478
 
        public Cloud getByName(String name) {
479
 
            for (Cloud c : this)
480
 
                if (c.name.equals(name))
481
 
                    return c;
482
 
            return null;
483
 
        }
484
 
 
485
 
        @Override
486
 
        protected void onModified() throws IOException {
487
 
            super.onModified();
488
 
            Jenkins.getInstance().trimLabels();
489
 
        }
490
 
    }
491
 
 
492
 
    /**
493
 
     * Set of installed cluster nodes.
494
 
     * <p>
495
 
     * We use this field with copy-on-write semantics.
496
 
     * This field has mutable list (to keep the serialization look clean),
497
 
     * but it shall never be modified. Only new completely populated slave
498
 
     * list can be set here.
499
 
     * <p>
500
 
     * The field name should be really {@code nodes}, but again the backward compatibility
501
 
     * prevents us from renaming.
502
 
     */
503
 
    protected volatile NodeList slaves;
504
 
 
505
 
    /**
506
 
     * Quiet period.
507
 
     *
508
 
     * This is {@link Integer} so that we can initialize it to '5' for upgrading users.
509
 
     */
510
 
    /*package*/ Integer quietPeriod;
511
 
 
512
 
    /**
513
 
     * Global default for {@link AbstractProject#getScmCheckoutRetryCount()}
514
 
     */
515
 
    /*package*/ int scmCheckoutRetryCount;
516
 
 
517
 
    /**
518
 
     * {@link View}s.
519
 
     */
520
 
    private final CopyOnWriteArrayList<View> views = new CopyOnWriteArrayList<View>();
521
 
 
522
 
    /**
523
 
     * Name of the primary view.
524
 
     * <p>
525
 
     * Start with null, so that we can upgrade pre-1.269 data well.
526
 
     * @since 1.269
527
 
     */
528
 
    private volatile String primaryView;
529
 
 
530
 
    private transient final ViewGroupMixIn viewGroupMixIn = new ViewGroupMixIn(this) {
531
 
        protected List<View> views() { return views; }
532
 
        protected String primaryView() { return primaryView; }
533
 
        protected void primaryView(String name) { primaryView=name; }
534
 
    };
535
 
 
536
 
 
537
 
    private transient final FingerprintMap fingerprintMap = new FingerprintMap();
538
 
 
539
 
    /**
540
 
     * Loaded plugins.
541
 
     */
542
 
    public transient final PluginManager pluginManager;
543
 
 
544
 
    public transient volatile TcpSlaveAgentListener tcpSlaveAgentListener;
545
 
 
546
 
    private transient UDPBroadcastThread udpBroadcastThread;
547
 
 
548
 
    private transient DNSMultiCast dnsMultiCast;
549
 
 
550
 
    /**
551
 
     * List of registered {@link SCMListener}s.
552
 
     */
553
 
    private transient final CopyOnWriteList<SCMListener> scmListeners = new CopyOnWriteList<SCMListener>();
554
 
 
555
 
    /**
556
 
     * TCP slave agent port.
557
 
     * 0 for random, -1 to disable.
558
 
     */
559
 
    private int slaveAgentPort =0;
560
 
 
561
 
    /**
562
 
     * Whitespace-separated labels assigned to the master as a {@link Node}.
563
 
     */
564
 
    private String label="";
565
 
 
566
 
    /**
567
 
     * {@link hudson.security.csrf.CrumbIssuer}
568
 
     */
569
 
    private volatile CrumbIssuer crumbIssuer;
570
 
 
571
 
    /**
572
 
     * All labels known to Jenkins. This allows us to reuse the same label instances
573
 
     * as much as possible, even though that's not a strict requirement.
574
 
     */
575
 
    private transient final ConcurrentHashMap<String,Label> labels = new ConcurrentHashMap<String,Label>();
576
 
 
577
 
    /**
578
 
     * Load statistics of the entire system.
579
 
     *
580
 
     * This includes every executor and every job in the system.
581
 
     */
582
 
    @Exported
583
 
    public transient final OverallLoadStatistics overallLoad = new OverallLoadStatistics();
584
 
 
585
 
    /**
586
 
     * Load statistics of the free roaming jobs and slaves.
587
 
     * 
588
 
     * This includes all executors on {@link Mode#NORMAL} nodes and jobs that do not have any assigned nodes.
589
 
     *
590
 
     * @since 1.467
591
 
     */
592
 
    @Exported
593
 
    public transient final LoadStatistics unlabeledLoad = new UnlabeledLoadStatistics();
594
 
 
595
 
    /**
596
 
     * {@link NodeProvisioner} that reacts to {@link #unlabeledLoad}.
597
 
     * @since 1.467
598
 
     */
599
 
    public transient final NodeProvisioner unlabeledNodeProvisioner = new NodeProvisioner(null,unlabeledLoad);
600
 
 
601
 
    /**
602
 
     * @deprecated as of 1.467
603
 
     *      Use {@link #unlabeledNodeProvisioner}.
604
 
     *      This was broken because it was tracking all the executors in the system, but it was only tracking
605
 
     *      free-roaming jobs in the queue. So {@link Cloud} fails to launch nodes when you have some exclusive
606
 
     *      slaves and free-roaming jobs in the queue.
607
 
     */
608
 
    @Restricted(NoExternalUse.class)
609
 
    public transient final NodeProvisioner overallNodeProvisioner = unlabeledNodeProvisioner;
610
 
 
611
 
 
612
 
    public transient final ServletContext servletContext;
613
 
 
614
 
    /**
615
 
     * Transient action list. Useful for adding navigation items to the navigation bar
616
 
     * on the left.
617
 
     */
618
 
    private transient final List<Action> actions = new CopyOnWriteArrayList<Action>();
619
 
 
620
 
    /**
621
 
     * List of master node properties
622
 
     */
623
 
    private DescribableList<NodeProperty<?>,NodePropertyDescriptor> nodeProperties = new DescribableList<NodeProperty<?>,NodePropertyDescriptor>(this);
624
 
 
625
 
    /**
626
 
     * List of global properties
627
 
     */
628
 
    private DescribableList<NodeProperty<?>,NodePropertyDescriptor> globalNodeProperties = new DescribableList<NodeProperty<?>,NodePropertyDescriptor>(this);
629
 
 
630
 
    /**
631
 
     * {@link AdministrativeMonitor}s installed on this system.
632
 
     *
633
 
     * @see AdministrativeMonitor
634
 
     */
635
 
    public transient final List<AdministrativeMonitor> administrativeMonitors = getExtensionList(AdministrativeMonitor.class);
636
 
 
637
 
    /**
638
 
     * Widgets on Hudson.
639
 
     */
640
 
    private transient final List<Widget> widgets = getExtensionList(Widget.class);
641
 
 
642
 
    /**
643
 
     * {@link AdjunctManager}
644
 
     */
645
 
    private transient final AdjunctManager adjuncts;
646
 
 
647
 
    /**
648
 
     * Code that handles {@link ItemGroup} work.
649
 
     */
650
 
    private transient final ItemGroupMixIn itemGroupMixIn = new ItemGroupMixIn(this,this) {
651
 
        @Override
652
 
        protected void add(TopLevelItem item) {
653
 
            items.put(item.getName(),item);
654
 
        }
655
 
 
656
 
        @Override
657
 
        protected File getRootDirFor(String name) {
658
 
            return Jenkins.this.getRootDirFor(name);
659
 
        }
660
 
 
661
 
        /**
662
 
         * Send the browser to the config page.
663
 
         * use View to trim view/{default-view} from URL if possible
664
 
         */
665
 
        @Override
666
 
        protected String redirectAfterCreateItem(StaplerRequest req, TopLevelItem result) throws IOException {
667
 
            String redirect = result.getUrl()+"configure";
668
 
            List<Ancestor> ancestors = req.getAncestors();
669
 
            for (int i = ancestors.size() - 1; i >= 0; i--) {
670
 
                Object o = ancestors.get(i).getObject();
671
 
                if (o instanceof View) {
672
 
                    redirect = req.getContextPath() + '/' + ((View)o).getUrl() + redirect;
673
 
                    break;
674
 
                }
675
 
            }
676
 
            return redirect;
677
 
        }
678
 
    };
679
 
 
680
 
 
681
 
    /**
682
 
     * Hook for a test harness to intercept Jenkins.getInstance()
683
 
     *
684
 
     * Do not use in the production code as the signature may change.
685
 
     */
686
 
    public interface JenkinsHolder {
687
 
        Jenkins getInstance();
688
 
    }
689
 
 
690
 
    static JenkinsHolder HOLDER = new JenkinsHolder() {
691
 
        public Jenkins getInstance() {
692
 
            return theInstance;
693
 
        }
694
 
    };
695
 
 
696
 
    @CLIResolver
697
 
    public static Jenkins getInstance() {
698
 
        return HOLDER.getInstance();
699
 
    }
700
 
 
701
 
    /**
702
 
     * Secret key generated once and used for a long time, beyond
703
 
     * container start/stop. Persisted outside <tt>config.xml</tt> to avoid
704
 
     * accidental exposure.
705
 
     */
706
 
    private transient final String secretKey;
707
 
 
708
 
    private transient final UpdateCenter updateCenter = new UpdateCenter();
709
 
 
710
 
    /**
711
 
     * True if the user opted out from the statistics tracking. We'll never send anything if this is true.
712
 
     */
713
 
    private Boolean noUsageStatistics;
714
 
 
715
 
    /**
716
 
     * HTTP proxy configuration.
717
 
     */
718
 
    public transient volatile ProxyConfiguration proxy;
719
 
 
720
 
    /**
721
 
     * Bound to "/log".
722
 
     */
723
 
    private transient final LogRecorderManager log = new LogRecorderManager();
724
 
 
725
 
    protected Jenkins(File root, ServletContext context) throws IOException, InterruptedException, ReactorException {
726
 
        this(root,context,null);
727
 
    }
728
 
 
729
 
    /**
730
 
     * @param pluginManager
731
 
     *      If non-null, use existing plugin manager.  create a new one.
732
 
     */
733
 
    @edu.umd.cs.findbugs.annotations.SuppressWarnings("SC_START_IN_CTOR") // bug in FindBugs. It flags UDPBroadcastThread.start() call but that's for another class
734
 
    protected Jenkins(File root, ServletContext context, PluginManager pluginManager) throws IOException, InterruptedException, ReactorException {
735
 
        long start = System.currentTimeMillis();
736
 
        
737
 
        // As Jenkins is starting, grant this process full control
738
 
        ACL.impersonate(ACL.SYSTEM);
739
 
        try {
740
 
            this.root = root;
741
 
            this.servletContext = context;
742
 
            computeVersion(context);
743
 
            if(theInstance!=null)
744
 
                throw new IllegalStateException("second instance");
745
 
            theInstance = this;
746
 
 
747
 
            if (!new File(root,"jobs").exists()) {
748
 
                // if this is a fresh install, use more modern default layout that's consistent with slaves
749
 
                workspaceDir = "${JENKINS_HOME}/workspace/${ITEM_FULLNAME}";
750
 
            }
751
 
 
752
 
            // doing this early allows InitStrategy to set environment upfront
753
 
            final InitStrategy is = InitStrategy.get(Thread.currentThread().getContextClassLoader());
754
 
 
755
 
            Trigger.timer = new Timer("Jenkins cron thread");
756
 
            queue = new Queue(CONSISTENT_HASH?LoadBalancer.CONSISTENT_HASH:LoadBalancer.DEFAULT);
757
 
 
758
 
            try {
759
 
                dependencyGraph = DependencyGraph.EMPTY;
760
 
            } catch (InternalError e) {
761
 
                if(e.getMessage().contains("window server")) {
762
 
                    throw new Error("Looks like the server runs without X. Please specify -Djava.awt.headless=true as JVM option",e);
763
 
                }
764
 
                throw e;
765
 
            }
766
 
 
767
 
            // get or create the secret
768
 
            TextFile secretFile = new TextFile(new File(getRootDir(),"secret.key"));
769
 
            if(secretFile.exists()) {
770
 
                secretKey = secretFile.readTrim();
771
 
            } else {
772
 
                SecureRandom sr = new SecureRandom();
773
 
                byte[] random = new byte[32];
774
 
                sr.nextBytes(random);
775
 
                secretKey = Util.toHexString(random);
776
 
                secretFile.write(secretKey);
777
 
 
778
 
                // this marker indicates that the secret.key is generated by the version of Jenkins post SECURITY-49.
779
 
                // this indicates that there's no need to rewrite secrets on disk
780
 
                new FileBoolean(new File(root,"secret.key.not-so-secret")).on();
781
 
            }
782
 
 
783
 
            try {
784
 
                proxy = ProxyConfiguration.load();
785
 
            } catch (IOException e) {
786
 
                LOGGER.log(SEVERE, "Failed to load proxy configuration", e);
787
 
            }
788
 
 
789
 
            if (pluginManager==null)
790
 
                pluginManager = new LocalPluginManager(this);
791
 
            this.pluginManager = pluginManager;
792
 
            // JSON binding needs to be able to see all the classes from all the plugins
793
 
            WebApp.get(servletContext).setClassLoader(pluginManager.uberClassLoader);
794
 
 
795
 
            adjuncts = new AdjunctManager(servletContext, pluginManager.uberClassLoader,"adjuncts/"+SESSION_HASH);
796
 
 
797
 
            // initialization consists of ...
798
 
            executeReactor( is,
799
 
                    pluginManager.initTasks(is),    // loading and preparing plugins
800
 
                    loadTasks(),                    // load jobs
801
 
                    InitMilestone.ordering()        // forced ordering among key milestones
802
 
            );
803
 
 
804
 
            if(KILL_AFTER_LOAD)
805
 
                System.exit(0);
806
 
 
807
 
            if(slaveAgentPort!=-1) {
808
 
                try {
809
 
                    tcpSlaveAgentListener = new TcpSlaveAgentListener(slaveAgentPort);
810
 
                } catch (BindException e) {
811
 
                    new AdministrativeError(getClass().getName()+".tcpBind",
812
 
                            "Failed to listen to incoming slave connection",
813
 
                            "Failed to listen to incoming slave connection. <a href='configure'>Change the port number</a> to solve the problem.",e);
814
 
                }
815
 
            } else
816
 
                tcpSlaveAgentListener = null;
817
 
 
818
 
            try {
819
 
                udpBroadcastThread = new UDPBroadcastThread(this);
820
 
                udpBroadcastThread.start();
821
 
            } catch (IOException e) {
822
 
                LOGGER.log(Level.WARNING, "Failed to broadcast over UDP",e);
823
 
            }
824
 
            dnsMultiCast = new DNSMultiCast(this);
825
 
 
826
 
            Trigger.timer.scheduleAtFixedRate(new SafeTimerTask() {
827
 
                @Override
828
 
                protected void doRun() throws Exception {
829
 
                    trimLabels();
830
 
                }
831
 
            }, TimeUnit2.MINUTES.toMillis(5), TimeUnit2.MINUTES.toMillis(5));
832
 
 
833
 
            updateComputerList();
834
 
 
835
 
            {// master is online now
836
 
                Computer c = toComputer();
837
 
                if(c!=null)
838
 
                    for (ComputerListener cl : ComputerListener.all())
839
 
                        cl.onOnline(c,StreamTaskListener.fromStdout());
840
 
            }
841
 
 
842
 
            for (ItemListener l : ItemListener.all()) {
843
 
                long itemListenerStart = System.currentTimeMillis();
844
 
                l.onLoaded();
845
 
                if (LOG_STARTUP_PERFORMANCE)
846
 
                    LOGGER.info(String.format("Took %dms for item listener %s startup",
847
 
                            System.currentTimeMillis()-itemListenerStart,l.getClass().getName()));
848
 
            }
849
 
            
850
 
            if (LOG_STARTUP_PERFORMANCE)
851
 
                LOGGER.info(String.format("Took %dms for complete Jenkins startup",
852
 
                        System.currentTimeMillis()-start));
853
 
        } finally {
854
 
            SecurityContextHolder.clearContext();
855
 
        }
856
 
    }
857
 
 
858
 
    /**
859
 
     * Executes a reactor.
860
 
     *
861
 
     * @param is
862
 
     *      If non-null, this can be consulted for ignoring some tasks. Only used during the initialization of Hudson.
863
 
     */
864
 
    private void executeReactor(final InitStrategy is, TaskBuilder... builders) throws IOException, InterruptedException, ReactorException {
865
 
        Reactor reactor = new Reactor(builders) {
866
 
            /**
867
 
             * Sets the thread name to the task for better diagnostics.
868
 
             */
869
 
            @Override
870
 
            protected void runTask(Task task) throws Exception {
871
 
                if (is!=null && is.skipInitTask(task))  return;
872
 
 
873
 
                ACL.impersonate(ACL.SYSTEM); // full access in the initialization thread
874
 
                String taskName = task.getDisplayName();
875
 
 
876
 
                Thread t = Thread.currentThread();
877
 
                String name = t.getName();
878
 
                if (taskName !=null)
879
 
                    t.setName(taskName);
880
 
                try {
881
 
                    long start = System.currentTimeMillis();
882
 
                    super.runTask(task);
883
 
                    if(LOG_STARTUP_PERFORMANCE)
884
 
                        LOGGER.info(String.format("Took %dms for %s by %s",
885
 
                                System.currentTimeMillis()-start, taskName, name));
886
 
                } finally {
887
 
                    t.setName(name);
888
 
                    SecurityContextHolder.clearContext();
889
 
                }
890
 
            }
891
 
        };
892
 
 
893
 
        new InitReactorRunner() {
894
 
            @Override
895
 
            protected void onInitMilestoneAttained(InitMilestone milestone) {
896
 
                initLevel = milestone;
897
 
            }
898
 
        }.run(reactor);
899
 
    }
900
 
 
901
 
 
902
 
    public TcpSlaveAgentListener getTcpSlaveAgentListener() {
903
 
        return tcpSlaveAgentListener;
904
 
    }
905
 
 
906
 
    /**
907
 
     * Makes {@link AdjunctManager} URL-bound.
908
 
     * The dummy parameter allows us to use different URLs for the same adjunct,
909
 
     * for proper cache handling.
910
 
     */
911
 
    public AdjunctManager getAdjuncts(String dummy) {
912
 
        return adjuncts;
913
 
    }
914
 
 
915
 
    @Exported
916
 
    public int getSlaveAgentPort() {
917
 
        return slaveAgentPort;
918
 
    }
919
 
 
920
 
    /**
921
 
     * @param port
922
 
     *      0 to indicate random available TCP port. -1 to disable this service.
923
 
     */
924
 
    public void setSlaveAgentPort(int port) throws IOException {
925
 
        this.slaveAgentPort = port;
926
 
 
927
 
        // relaunch the agent
928
 
        if(tcpSlaveAgentListener==null) {
929
 
            if(slaveAgentPort!=-1)
930
 
                tcpSlaveAgentListener = new TcpSlaveAgentListener(slaveAgentPort);
931
 
        } else {
932
 
            if(tcpSlaveAgentListener.configuredPort!=slaveAgentPort) {
933
 
                tcpSlaveAgentListener.shutdown();
934
 
                tcpSlaveAgentListener = null;
935
 
                if(slaveAgentPort!=-1)
936
 
                    tcpSlaveAgentListener = new TcpSlaveAgentListener(slaveAgentPort);
937
 
            }
938
 
        }
939
 
    }
940
 
 
941
 
    public void setNodeName(String name) {
942
 
        throw new UnsupportedOperationException(); // not allowed
943
 
    }
944
 
 
945
 
    public String getNodeDescription() {
946
 
        return Messages.Hudson_NodeDescription();
947
 
    }
948
 
 
949
 
    @Exported
950
 
    public String getDescription() {
951
 
        return systemMessage;
952
 
    }
953
 
 
954
 
    public PluginManager getPluginManager() {
955
 
        return pluginManager;
956
 
    }
957
 
 
958
 
    public UpdateCenter getUpdateCenter() {
959
 
        return updateCenter;
960
 
    }
961
 
 
962
 
    public boolean isUsageStatisticsCollected() {
963
 
        return noUsageStatistics==null || !noUsageStatistics;
964
 
    }
965
 
 
966
 
    public void setNoUsageStatistics(Boolean noUsageStatistics) throws IOException {
967
 
        this.noUsageStatistics = noUsageStatistics;
968
 
        save();
969
 
    }
970
 
 
971
 
    public View.People getPeople() {
972
 
        return new View.People(this);
973
 
    }
974
 
 
975
 
    /**
976
 
     * @since 1.484
977
 
     */
978
 
    public View.AsynchPeople getAsynchPeople() {
979
 
        return new View.AsynchPeople(this);
980
 
    }
981
 
 
982
 
    /**
983
 
     * Does this {@link View} has any associated user information recorded?
984
 
     * @deprecated Potentially very expensive call; do not use from Jelly views.
985
 
     */
986
 
    public boolean hasPeople() {
987
 
        return View.People.isApplicable(items.values());
988
 
    }
989
 
 
990
 
    public Api getApi() {
991
 
        return new Api(this);
992
 
    }
993
 
 
994
 
    /**
995
 
     * Returns a secret key that survives across container start/stop.
996
 
     * <p>
997
 
     * This value is useful for implementing some of the security features.
998
 
     *
999
 
     * @deprecated
1000
 
     *      Due to the past security advisory, this value should not be used any more to protect sensitive information.
1001
 
     *      See {@link ConfidentialStore} and {@link ConfidentialKey} for how to store secrets.
1002
 
     */
1003
 
    public String getSecretKey() {
1004
 
        return secretKey;
1005
 
    }
1006
 
 
1007
 
    /**
1008
 
     * Gets {@linkplain #getSecretKey() the secret key} as a key for AES-128.
1009
 
     * @since 1.308
1010
 
     * @deprecated
1011
 
     *       See {@link #getSecretKey()}.
1012
 
     */
1013
 
    public SecretKey getSecretKeyAsAES128() {
1014
 
        return Util.toAes128Key(secretKey);
1015
 
    }
1016
 
 
1017
 
    /**
1018
 
     * Returns the unique identifier of this Jenkins that has been historically used to identify
1019
 
     * this Jenkins to the outside world.
1020
 
     *
1021
 
     * <p>
1022
 
     * This form of identifier is weak in that it can be impersonated by others. See
1023
 
     * https://wiki.jenkins-ci.org/display/JENKINS/Instance+Identity for more modern form of instance ID
1024
 
     * that can be challenged and verified.
1025
 
     *
1026
 
     * @since 1.498
1027
 
     */
1028
 
    @SuppressWarnings("deprecation")
1029
 
    public String getLegacyInstanceId() {
1030
 
        return Util.getDigestOf(getSecretKey());
1031
 
    }
1032
 
 
1033
 
    /**
1034
 
     * Gets the SCM descriptor by name. Primarily used for making them web-visible.
1035
 
     */
1036
 
    public Descriptor<SCM> getScm(String shortClassName) {
1037
 
        return findDescriptor(shortClassName,SCM.all());
1038
 
    }
1039
 
 
1040
 
    /**
1041
 
     * Gets the repository browser descriptor by name. Primarily used for making them web-visible.
1042
 
     */
1043
 
    public Descriptor<RepositoryBrowser<?>> getRepositoryBrowser(String shortClassName) {
1044
 
        return findDescriptor(shortClassName,RepositoryBrowser.all());
1045
 
    }
1046
 
 
1047
 
    /**
1048
 
     * Gets the builder descriptor by name. Primarily used for making them web-visible.
1049
 
     */
1050
 
    public Descriptor<Builder> getBuilder(String shortClassName) {
1051
 
        return findDescriptor(shortClassName, Builder.all());
1052
 
    }
1053
 
 
1054
 
    /**
1055
 
     * Gets the build wrapper descriptor by name. Primarily used for making them web-visible.
1056
 
     */
1057
 
    public Descriptor<BuildWrapper> getBuildWrapper(String shortClassName) {
1058
 
        return findDescriptor(shortClassName, BuildWrapper.all());
1059
 
    }
1060
 
 
1061
 
    /**
1062
 
     * Gets the publisher descriptor by name. Primarily used for making them web-visible.
1063
 
     */
1064
 
    public Descriptor<Publisher> getPublisher(String shortClassName) {
1065
 
        return findDescriptor(shortClassName, Publisher.all());
1066
 
    }
1067
 
 
1068
 
    /**
1069
 
     * Gets the trigger descriptor by name. Primarily used for making them web-visible.
1070
 
     */
1071
 
    public TriggerDescriptor getTrigger(String shortClassName) {
1072
 
        return (TriggerDescriptor) findDescriptor(shortClassName, Trigger.all());
1073
 
    }
1074
 
 
1075
 
    /**
1076
 
     * Gets the retention strategy descriptor by name. Primarily used for making them web-visible.
1077
 
     */
1078
 
    public Descriptor<RetentionStrategy<?>> getRetentionStrategy(String shortClassName) {
1079
 
        return findDescriptor(shortClassName, RetentionStrategy.all());
1080
 
    }
1081
 
 
1082
 
    /**
1083
 
     * Gets the {@link JobPropertyDescriptor} by name. Primarily used for making them web-visible.
1084
 
     */
1085
 
    public JobPropertyDescriptor getJobProperty(String shortClassName) {
1086
 
        // combining these two lines triggers javac bug. See issue #610.
1087
 
        Descriptor d = findDescriptor(shortClassName, JobPropertyDescriptor.all());
1088
 
        return (JobPropertyDescriptor) d;
1089
 
    }
1090
 
 
1091
 
    /**
1092
 
     * @deprecated
1093
 
     *      UI method. Not meant to be used programatically.
1094
 
     */
1095
 
    public ComputerSet getComputer() {
1096
 
        return new ComputerSet();
1097
 
    }
1098
 
 
1099
 
    /**
1100
 
     * Exposes {@link Descriptor} by its name to URL.
1101
 
     *
1102
 
     * After doing all the {@code getXXX(shortClassName)} methods, I finally realized that
1103
 
     * this just doesn't scale.
1104
 
     *
1105
 
     * @param id
1106
 
     *      Either {@link Descriptor#getId()} (recommended) or the short name of a {@link Describable} subtype (for compatibility)
1107
 
     */
1108
 
    public Descriptor getDescriptor(String id) {
1109
 
        // legacy descriptors that are reigstered manually doesn't show up in getExtensionList, so check them explicitly.
1110
 
        for( Descriptor d : Iterators.sequence(getExtensionList(Descriptor.class),DescriptorExtensionList.listLegacyInstances()) ) {
1111
 
            String name = d.getId();
1112
 
            if(name.equals(id))
1113
 
                return d;
1114
 
            if(name.substring(name.lastIndexOf('.')+1).equals(id))
1115
 
                return d;
1116
 
        }
1117
 
        return null;
1118
 
    }
1119
 
 
1120
 
    /**
1121
 
     * Alias for {@link #getDescriptor(String)}.
1122
 
     */
1123
 
    public Descriptor getDescriptorByName(String id) {
1124
 
        return getDescriptor(id);
1125
 
    }
1126
 
 
1127
 
    /**
1128
 
     * Gets the {@link Descriptor} that corresponds to the given {@link Describable} type.
1129
 
     * <p>
1130
 
     * If you have an instance of {@code type} and call {@link Describable#getDescriptor()},
1131
 
     * you'll get the same instance that this method returns.
1132
 
     */
1133
 
    public Descriptor getDescriptor(Class<? extends Describable> type) {
1134
 
        for( Descriptor d : getExtensionList(Descriptor.class) )
1135
 
            if(d.clazz==type)
1136
 
                return d;
1137
 
        return null;
1138
 
    }
1139
 
 
1140
 
    /**
1141
 
     * Works just like {@link #getDescriptor(Class)} but don't take no for an answer.
1142
 
     *
1143
 
     * @throws AssertionError
1144
 
     *      If the descriptor is missing.
1145
 
     * @since 1.326
1146
 
     */
1147
 
    public Descriptor getDescriptorOrDie(Class<? extends Describable> type) {
1148
 
        Descriptor d = getDescriptor(type);
1149
 
        if (d==null)
1150
 
            throw new AssertionError(type+" is missing its descriptor");
1151
 
        return d;
1152
 
    }
1153
 
 
1154
 
    /**
1155
 
     * Gets the {@link Descriptor} instance in the current Hudson by its type.
1156
 
     */
1157
 
    public <T extends Descriptor> T getDescriptorByType(Class<T> type) {
1158
 
        for( Descriptor d : getExtensionList(Descriptor.class) )
1159
 
            if(d.getClass()==type)
1160
 
                return type.cast(d);
1161
 
        return null;
1162
 
    }
1163
 
 
1164
 
    /**
1165
 
     * Gets the {@link SecurityRealm} descriptors by name. Primarily used for making them web-visible.
1166
 
     */
1167
 
    public Descriptor<SecurityRealm> getSecurityRealms(String shortClassName) {
1168
 
        return findDescriptor(shortClassName,SecurityRealm.all());
1169
 
    }
1170
 
 
1171
 
    /**
1172
 
     * Finds a descriptor that has the specified name.
1173
 
     */
1174
 
    private <T extends Describable<T>>
1175
 
    Descriptor<T> findDescriptor(String shortClassName, Collection<? extends Descriptor<T>> descriptors) {
1176
 
        String name = '.'+shortClassName;
1177
 
        for (Descriptor<T> d : descriptors) {
1178
 
            if(d.clazz.getName().endsWith(name))
1179
 
                return d;
1180
 
        }
1181
 
        return null;
1182
 
    }
1183
 
 
1184
 
    protected void updateComputerList() throws IOException {
1185
 
        updateComputerList(AUTOMATIC_SLAVE_LAUNCH);
1186
 
    }
1187
 
 
1188
 
    /**
1189
 
     * Gets all the installed {@link SCMListener}s.
1190
 
     */
1191
 
    public CopyOnWriteList<SCMListener> getSCMListeners() {
1192
 
        return scmListeners;
1193
 
    }
1194
 
 
1195
 
    /**
1196
 
     * Gets the plugin object from its short name.
1197
 
     *
1198
 
     * <p>
1199
 
     * This allows URL <tt>hudson/plugin/ID</tt> to be served by the views
1200
 
     * of the plugin class.
1201
 
     */
1202
 
    public Plugin getPlugin(String shortName) {
1203
 
        PluginWrapper p = pluginManager.getPlugin(shortName);
1204
 
        if(p==null)     return null;
1205
 
        return p.getPlugin();
1206
 
    }
1207
 
 
1208
 
    /**
1209
 
     * Gets the plugin object from its class.
1210
 
     *
1211
 
     * <p>
1212
 
     * This allows easy storage of plugin information in the plugin singleton without
1213
 
     * every plugin reimplementing the singleton pattern.
1214
 
     *
1215
 
     * @param clazz The plugin class (beware class-loader fun, this will probably only work
1216
 
     * from within the jpi that defines the plugin class, it may or may not work in other cases)
1217
 
     *
1218
 
     * @return The plugin instance.
1219
 
     */
1220
 
    @SuppressWarnings("unchecked")
1221
 
    public <P extends Plugin> P getPlugin(Class<P> clazz) {
1222
 
        PluginWrapper p = pluginManager.getPlugin(clazz);
1223
 
        if(p==null)     return null;
1224
 
        return (P) p.getPlugin();
1225
 
    }
1226
 
 
1227
 
    /**
1228
 
     * Gets the plugin objects from their super-class.
1229
 
     *
1230
 
     * @param clazz The plugin class (beware class-loader fun)
1231
 
     *
1232
 
     * @return The plugin instances.
1233
 
     */
1234
 
    public <P extends Plugin> List<P> getPlugins(Class<P> clazz) {
1235
 
        List<P> result = new ArrayList<P>();
1236
 
        for (PluginWrapper w: pluginManager.getPlugins(clazz)) {
1237
 
            result.add((P)w.getPlugin());
1238
 
        }
1239
 
        return Collections.unmodifiableList(result);
1240
 
    }
1241
 
 
1242
 
    /**
1243
 
     * Synonym to {@link #getNodeDescription()}.
1244
 
     */
1245
 
    public String getSystemMessage() {
1246
 
        return systemMessage;
1247
 
    }
1248
 
 
1249
 
    /**
1250
 
     * Gets the markup formatter used in the system.
1251
 
     *
1252
 
     * @return
1253
 
     *      never null.
1254
 
     * @since 1.391
1255
 
     */
1256
 
    public MarkupFormatter getMarkupFormatter() {
1257
 
        return markupFormatter!=null ? markupFormatter : RawHtmlMarkupFormatter.INSTANCE;
1258
 
    }
1259
 
 
1260
 
    /**
1261
 
     * Sets the markup formatter used in the system globally.
1262
 
     *
1263
 
     * @since 1.391
1264
 
     */
1265
 
    public void setMarkupFormatter(MarkupFormatter f) {
1266
 
        this.markupFormatter = f;
1267
 
    }
1268
 
 
1269
 
    /**
1270
 
     * Sets the system message.
1271
 
     */
1272
 
    public void setSystemMessage(String message) throws IOException {
1273
 
        this.systemMessage = message;
1274
 
        save();
1275
 
    }
1276
 
 
1277
 
    public FederatedLoginService getFederatedLoginService(String name) {
1278
 
        for (FederatedLoginService fls : FederatedLoginService.all()) {
1279
 
            if (fls.getUrlName().equals(name))
1280
 
                return fls;
1281
 
        }
1282
 
        return null;
1283
 
    }
1284
 
 
1285
 
    public List<FederatedLoginService> getFederatedLoginServices() {
1286
 
        return FederatedLoginService.all();
1287
 
    }
1288
 
 
1289
 
    public Launcher createLauncher(TaskListener listener) {
1290
 
        return new LocalLauncher(listener).decorateFor(this);
1291
 
    }
1292
 
 
1293
 
 
1294
 
    public String getFullName() {
1295
 
        return "";
1296
 
    }
1297
 
 
1298
 
    public String getFullDisplayName() {
1299
 
        return "";
1300
 
    }
1301
 
 
1302
 
    /**
1303
 
     * Returns the transient {@link Action}s associated with the top page.
1304
 
     *
1305
 
     * <p>
1306
 
     * Adding {@link Action} is primarily useful for plugins to contribute
1307
 
     * an item to the navigation bar of the top page. See existing {@link Action}
1308
 
     * implementation for it affects the GUI.
1309
 
     *
1310
 
     * <p>
1311
 
     * To register an {@link Action}, implement {@link RootAction} extension point, or write code like
1312
 
     * {@code Hudson.getInstance().getActions().add(...)}.
1313
 
     *
1314
 
     * @return
1315
 
     *      Live list where the changes can be made. Can be empty but never null.
1316
 
     * @since 1.172
1317
 
     */
1318
 
    public List<Action> getActions() {
1319
 
        return actions;
1320
 
    }
1321
 
 
1322
 
    /**
1323
 
     * Gets just the immediate children of {@link Jenkins}.
1324
 
     *
1325
 
     * @see #getAllItems(Class)
1326
 
     */
1327
 
    @Exported(name="jobs")
1328
 
    public List<TopLevelItem> getItems() {
1329
 
                if (authorizationStrategy instanceof AuthorizationStrategy.Unsecured ||
1330
 
                        authorizationStrategy instanceof FullControlOnceLoggedInAuthorizationStrategy) {
1331
 
                        return new ArrayList(items.values());
1332
 
                }
1333
 
 
1334
 
        List<TopLevelItem> viewableItems = new ArrayList<TopLevelItem>();
1335
 
        for (TopLevelItem item : items.values()) {
1336
 
            if (item.hasPermission(Item.READ))
1337
 
                viewableItems.add(item);
1338
 
        }
1339
 
                
1340
 
        return viewableItems;
1341
 
    }
1342
 
 
1343
 
    /**
1344
 
     * Returns the read-only view of all the {@link TopLevelItem}s keyed by their names.
1345
 
     * <p>
1346
 
     * This method is efficient, as it doesn't involve any copying.
1347
 
     *
1348
 
     * @since 1.296
1349
 
     */
1350
 
    public Map<String,TopLevelItem> getItemMap() {
1351
 
        return Collections.unmodifiableMap(items);
1352
 
    }
1353
 
 
1354
 
    /**
1355
 
     * Gets just the immediate children of {@link Jenkins} but of the given type.
1356
 
     */
1357
 
    public <T> List<T> getItems(Class<T> type) {
1358
 
        List<T> r = new ArrayList<T>();
1359
 
        for (TopLevelItem i : getItems())
1360
 
            if (type.isInstance(i))
1361
 
                 r.add(type.cast(i));
1362
 
        return r;
1363
 
    }
1364
 
 
1365
 
    /**
1366
 
     * Gets all the {@link Item}s recursively in the {@link ItemGroup} tree
1367
 
     * and filter them by the given type.
1368
 
     */
1369
 
    public <T extends Item> List<T> getAllItems(Class<T> type) {
1370
 
        List<T> r = new ArrayList<T>();
1371
 
 
1372
 
        Stack<ItemGroup> q = new Stack<ItemGroup>();
1373
 
        q.push(this);
1374
 
 
1375
 
        while(!q.isEmpty()) {
1376
 
            ItemGroup<?> parent = q.pop();
1377
 
            for (Item i : parent.getItems()) {
1378
 
                if(type.isInstance(i)) {
1379
 
                    if (i.hasPermission(Item.READ))
1380
 
                        r.add(type.cast(i));
1381
 
                }
1382
 
                if(i instanceof ItemGroup)
1383
 
                    q.push((ItemGroup)i);
1384
 
            }
1385
 
        }
1386
 
 
1387
 
        return r;
1388
 
    }
1389
 
 
1390
 
    /**
1391
 
     * Gets all the items recursively.
1392
 
     *
1393
 
     * @since 1.402
1394
 
     */
1395
 
    public List<Item> getAllItems() {
1396
 
        return getAllItems(Item.class);
1397
 
    }
1398
 
 
1399
 
    /**
1400
 
     * Gets a list of simple top-level projects.
1401
 
     * @deprecated This method will ignore Maven and matrix projects, as well as projects inside containers such as folders.
1402
 
     * You may prefer to call {@link #getAllItems(Class)} on {@link AbstractProject},
1403
 
     * perhaps also using {@link Util#createSubList} to consider only {@link TopLevelItem}s.
1404
 
     * (That will also consider the caller's permissions.)
1405
 
     * If you really want to get just {@link Project}s at top level, ignoring permissions,
1406
 
     * you can filter the values from {@link #getItemMap} using {@link Util#createSubList}.
1407
 
     */
1408
 
    @Deprecated
1409
 
    public List<Project> getProjects() {
1410
 
        return Util.createSubList(items.values(),Project.class);
1411
 
    }
1412
 
 
1413
 
    /**
1414
 
     * Gets the names of all the {@link Job}s.
1415
 
     */
1416
 
    public Collection<String> getJobNames() {
1417
 
        List<String> names = new ArrayList<String>();
1418
 
        for (Job j : getAllItems(Job.class))
1419
 
            names.add(j.getFullName());
1420
 
        return names;
1421
 
    }
1422
 
 
1423
 
    public List<Action> getViewActions() {
1424
 
        return getActions();
1425
 
    }
1426
 
 
1427
 
    /**
1428
 
     * Gets the names of all the {@link TopLevelItem}s.
1429
 
     */
1430
 
    public Collection<String> getTopLevelItemNames() {
1431
 
        List<String> names = new ArrayList<String>();
1432
 
        for (TopLevelItem j : items.values())
1433
 
            names.add(j.getName());
1434
 
        return names;
1435
 
    }
1436
 
 
1437
 
    public View getView(String name) {
1438
 
        return viewGroupMixIn.getView(name);
1439
 
    }
1440
 
 
1441
 
    /**
1442
 
     * Gets the read-only list of all {@link View}s.
1443
 
     */
1444
 
    @Exported
1445
 
    public Collection<View> getViews() {
1446
 
        return viewGroupMixIn.getViews();
1447
 
    }
1448
 
 
1449
 
    public void addView(View v) throws IOException {
1450
 
        viewGroupMixIn.addView(v);
1451
 
    }
1452
 
 
1453
 
    public boolean canDelete(View view) {
1454
 
        return viewGroupMixIn.canDelete(view);
1455
 
    }
1456
 
 
1457
 
    public synchronized void deleteView(View view) throws IOException {
1458
 
        viewGroupMixIn.deleteView(view);
1459
 
    }
1460
 
 
1461
 
    public void onViewRenamed(View view, String oldName, String newName) {
1462
 
        viewGroupMixIn.onViewRenamed(view,oldName,newName);
1463
 
    }
1464
 
 
1465
 
    /**
1466
 
     * Returns the primary {@link View} that renders the top-page of Hudson.
1467
 
     */
1468
 
    @Exported
1469
 
    public View getPrimaryView() {
1470
 
        return viewGroupMixIn.getPrimaryView();
1471
 
     }
1472
 
 
1473
 
    public void setPrimaryView(View v) {
1474
 
        this.primaryView = v.getViewName();
1475
 
    }
1476
 
 
1477
 
    public ViewsTabBar getViewsTabBar() {
1478
 
        return viewsTabBar;
1479
 
    }
1480
 
 
1481
 
    public void setViewsTabBar(ViewsTabBar viewsTabBar) {
1482
 
        this.viewsTabBar = viewsTabBar;
1483
 
    }
1484
 
 
1485
 
    public Jenkins getItemGroup() {
1486
 
        return this;
1487
 
   }
1488
 
 
1489
 
    public MyViewsTabBar getMyViewsTabBar() {
1490
 
        return myViewsTabBar;
1491
 
    }
1492
 
 
1493
 
    public void setMyViewsTabBar(MyViewsTabBar myViewsTabBar) {
1494
 
        this.myViewsTabBar = myViewsTabBar;
1495
 
    }
1496
 
 
1497
 
    /**
1498
 
     * Returns true if the current running Hudson is upgraded from a version earlier than the specified version.
1499
 
     *
1500
 
     * <p>
1501
 
     * This method continues to return true until the system configuration is saved, at which point
1502
 
     * {@link #version} will be overwritten and Hudson forgets the upgrade history.
1503
 
     *
1504
 
     * <p>
1505
 
     * To handle SNAPSHOTS correctly, pass in "1.N.*" to test if it's upgrading from the version
1506
 
     * equal or younger than N. So say if you implement a feature in 1.301 and you want to check
1507
 
     * if the installation upgraded from pre-1.301, pass in "1.300.*"
1508
 
     *
1509
 
     * @since 1.301
1510
 
     */
1511
 
    public boolean isUpgradedFromBefore(VersionNumber v) {
1512
 
        try {
1513
 
            return new VersionNumber(version).isOlderThan(v);
1514
 
        } catch (IllegalArgumentException e) {
1515
 
            // fail to parse this version number
1516
 
            return false;
1517
 
        }
1518
 
    }
1519
 
 
1520
 
    /**
1521
 
     * Gets the read-only list of all {@link Computer}s.
1522
 
     */
1523
 
    public Computer[] getComputers() {
1524
 
        Computer[] r = computers.values().toArray(new Computer[computers.size()]);
1525
 
        Arrays.sort(r,new Comparator<Computer>() {
1526
 
            final Collator collator = Collator.getInstance();
1527
 
            public int compare(Computer lhs, Computer rhs) {
1528
 
                if(lhs.getNode()==Jenkins.this)  return -1;
1529
 
                if(rhs.getNode()==Jenkins.this)  return 1;
1530
 
                return collator.compare(lhs.getDisplayName(), rhs.getDisplayName());
1531
 
            }
1532
 
        });
1533
 
        return r;
1534
 
    }
1535
 
 
1536
 
    @CLIResolver
1537
 
    public Computer getComputer(@Argument(required=true,metaVar="NAME",usage="Node name") String name) {
1538
 
        if(name.equals("(master)"))
1539
 
            name = "";
1540
 
 
1541
 
        for (Computer c : computers.values()) {
1542
 
            if(c.getName().equals(name))
1543
 
                return c;
1544
 
        }
1545
 
        return null;
1546
 
    }
1547
 
 
1548
 
    /**
1549
 
     * Gets the label that exists on this system by the name.
1550
 
     *
1551
 
     * @return null if name is null.
1552
 
     * @see Label#parseExpression(String) (String)
1553
 
     */
1554
 
    public Label getLabel(String expr) {
1555
 
        if(expr==null)  return null;
1556
 
        while(true) {
1557
 
            Label l = labels.get(expr);
1558
 
            if(l!=null)
1559
 
                return l;
1560
 
 
1561
 
            // non-existent
1562
 
            try {
1563
 
                labels.putIfAbsent(expr,Label.parseExpression(expr));
1564
 
            } catch (ANTLRException e) {
1565
 
                // laxly accept it as a single label atom for backward compatibility
1566
 
                return getLabelAtom(expr);
1567
 
            }
1568
 
        }
1569
 
    }
1570
 
 
1571
 
    /**
1572
 
     * Returns the label atom of the given name.
1573
 
     */
1574
 
    public LabelAtom getLabelAtom(String name) {
1575
 
        if (name==null)  return null;
1576
 
 
1577
 
        while(true) {
1578
 
            Label l = labels.get(name);
1579
 
            if(l!=null)
1580
 
                return (LabelAtom)l;
1581
 
 
1582
 
            // non-existent
1583
 
            LabelAtom la = new LabelAtom(name);
1584
 
            if (labels.putIfAbsent(name, la)==null)
1585
 
                la.load();
1586
 
        }
1587
 
    }
1588
 
 
1589
 
    /**
1590
 
     * Gets all the active labels in the current system.
1591
 
     */
1592
 
    public Set<Label> getLabels() {
1593
 
        Set<Label> r = new TreeSet<Label>();
1594
 
        for (Label l : labels.values()) {
1595
 
            if(!l.isEmpty())
1596
 
                r.add(l);
1597
 
        }
1598
 
        return r;
1599
 
    }
1600
 
 
1601
 
    public Set<LabelAtom> getLabelAtoms() {
1602
 
        Set<LabelAtom> r = new TreeSet<LabelAtom>();
1603
 
        for (Label l : labels.values()) {
1604
 
            if(!l.isEmpty() && l instanceof LabelAtom)
1605
 
                r.add((LabelAtom)l);
1606
 
        }
1607
 
        return r;
1608
 
    }
1609
 
 
1610
 
    public Queue getQueue() {
1611
 
        return queue;
1612
 
    }
1613
 
 
1614
 
    @Override
1615
 
    public String getDisplayName() {
1616
 
        return Messages.Hudson_DisplayName();
1617
 
    }
1618
 
 
1619
 
    public List<JDK> getJDKs() {
1620
 
        if(jdks==null)
1621
 
            jdks = new ArrayList<JDK>();
1622
 
        return jdks;
1623
 
    }
1624
 
 
1625
 
    /**
1626
 
     * Gets the JDK installation of the given name, or returns null.
1627
 
     */
1628
 
    public JDK getJDK(String name) {
1629
 
        if(name==null) {
1630
 
            // if only one JDK is configured, "default JDK" should mean that JDK.
1631
 
            List<JDK> jdks = getJDKs();
1632
 
            if(jdks.size()==1)  return jdks.get(0);
1633
 
            return null;
1634
 
        }
1635
 
        for (JDK j : getJDKs()) {
1636
 
            if(j.getName().equals(name))
1637
 
                return j;
1638
 
        }
1639
 
        return null;
1640
 
    }
1641
 
 
1642
 
 
1643
 
 
1644
 
    /**
1645
 
     * Gets the slave node of the give name, hooked under this Hudson.
1646
 
     */
1647
 
    public Node getNode(String name) {
1648
 
        return slaves.getNode(name);
1649
 
    }
1650
 
 
1651
 
    /**
1652
 
     * Gets a {@link Cloud} by {@link Cloud#name its name}, or null.
1653
 
     */
1654
 
    public Cloud getCloud(String name) {
1655
 
        return clouds.getByName(name);
1656
 
    }
1657
 
 
1658
 
    protected Map<Node,Computer> getComputerMap() {
1659
 
        return computers;
1660
 
    }
1661
 
 
1662
 
    /**
1663
 
     * Returns all {@link Node}s in the system, excluding {@link Jenkins} instance itself which
1664
 
     * represents the master.
1665
 
     */
1666
 
    public List<Node> getNodes() {
1667
 
        return slaves;
1668
 
    }
1669
 
 
1670
 
    /**
1671
 
     * Adds one more {@link Node} to Hudson.
1672
 
     */
1673
 
    public synchronized void addNode(Node n) throws IOException {
1674
 
        if(n==null)     throw new IllegalArgumentException();
1675
 
        ArrayList<Node> nl = new ArrayList<Node>(this.slaves);
1676
 
        if(!nl.contains(n)) // defensive check
1677
 
            nl.add(n);
1678
 
        setNodes(nl);
1679
 
    }
1680
 
 
1681
 
    /**
1682
 
     * Removes a {@link Node} from Hudson.
1683
 
     */
1684
 
    public synchronized void removeNode(Node n) throws IOException {
1685
 
        Computer c = n.toComputer();
1686
 
        if (c!=null)
1687
 
            c.disconnect(OfflineCause.create(Messages._Hudson_NodeBeingRemoved()));
1688
 
 
1689
 
        ArrayList<Node> nl = new ArrayList<Node>(this.slaves);
1690
 
        nl.remove(n);
1691
 
        setNodes(nl);
1692
 
    }
1693
 
 
1694
 
    public void setNodes(List<? extends Node> nodes) throws IOException {
1695
 
        this.slaves = new NodeList(nodes);
1696
 
        updateComputerList();
1697
 
        trimLabels();
1698
 
        save();
1699
 
    }
1700
 
 
1701
 
    public DescribableList<NodeProperty<?>, NodePropertyDescriptor> getNodeProperties() {
1702
 
        return nodeProperties;
1703
 
    }
1704
 
 
1705
 
    public DescribableList<NodeProperty<?>, NodePropertyDescriptor> getGlobalNodeProperties() {
1706
 
        return globalNodeProperties;
1707
 
    }
1708
 
 
1709
 
    /**
1710
 
     * Resets all labels and remove invalid ones.
1711
 
     *
1712
 
     * This should be called when the assumptions behind label cache computation changes,
1713
 
     * but we also call this periodically to self-heal any data out-of-sync issue.
1714
 
     */
1715
 
    private void trimLabels() {
1716
 
        for (Iterator<Label> itr = labels.values().iterator(); itr.hasNext();) {
1717
 
            Label l = itr.next();
1718
 
            resetLabel(l);
1719
 
            if(l.isEmpty())
1720
 
                itr.remove();
1721
 
        }
1722
 
    }
1723
 
 
1724
 
    /**
1725
 
     * Binds {@link AdministrativeMonitor}s to URL.
1726
 
     */
1727
 
    public AdministrativeMonitor getAdministrativeMonitor(String id) {
1728
 
        for (AdministrativeMonitor m : administrativeMonitors)
1729
 
            if(m.id.equals(id))
1730
 
                return m;
1731
 
        return null;
1732
 
    }
1733
 
 
1734
 
    public NodeDescriptor getDescriptor() {
1735
 
        return DescriptorImpl.INSTANCE;
1736
 
    }
1737
 
 
1738
 
    public static final class DescriptorImpl extends NodeDescriptor {
1739
 
        @Extension
1740
 
        public static final DescriptorImpl INSTANCE = new DescriptorImpl();
1741
 
 
1742
 
        public String getDisplayName() {
1743
 
            return "";
1744
 
        }
1745
 
 
1746
 
        @Override
1747
 
        public boolean isInstantiable() {
1748
 
            return false;
1749
 
        }
1750
 
 
1751
 
        public FormValidation doCheckNumExecutors(@QueryParameter String value) {
1752
 
            return FormValidation.validateNonNegativeInteger(value);
1753
 
        }
1754
 
 
1755
 
        // to route /descriptor/FQCN/xxx to getDescriptor(FQCN).xxx
1756
 
        public Object getDynamic(String token) {
1757
 
            return Jenkins.getInstance().getDescriptor(token);
1758
 
        }
1759
 
    }
1760
 
 
1761
 
    /**
1762
 
     * Gets the system default quiet period.
1763
 
     */
1764
 
    public int getQuietPeriod() {
1765
 
        return quietPeriod!=null ? quietPeriod : 5;
1766
 
    }
1767
 
 
1768
 
    /**
1769
 
     * Sets the global quiet period.
1770
 
     *
1771
 
     * @param quietPeriod
1772
 
     *      null to the default value.
1773
 
     */
1774
 
    public void setQuietPeriod(Integer quietPeriod) throws IOException {
1775
 
        this.quietPeriod = quietPeriod;
1776
 
        save();
1777
 
    }
1778
 
 
1779
 
    /**
1780
 
     * Gets the global SCM check out retry count.
1781
 
     */
1782
 
    public int getScmCheckoutRetryCount() {
1783
 
        return scmCheckoutRetryCount;
1784
 
    }
1785
 
 
1786
 
    public void setScmCheckoutRetryCount(int scmCheckoutRetryCount) throws IOException {
1787
 
        this.scmCheckoutRetryCount = scmCheckoutRetryCount;
1788
 
        save();
1789
 
    }
1790
 
 
1791
 
    @Override
1792
 
    public String getSearchUrl() {
1793
 
        return "";
1794
 
    }
1795
 
 
1796
 
    @Override
1797
 
    public SearchIndexBuilder makeSearchIndex() {
1798
 
        return super.makeSearchIndex()
1799
 
            .add("configure", "config","configure")
1800
 
            .add("manage")
1801
 
            .add("log")
1802
 
            .add(new CollectionSearchIndex<TopLevelItem>() {
1803
 
                protected SearchItem get(String key) { return getItem(key); }
1804
 
                protected Collection<TopLevelItem> all() { return getItems(); }
1805
 
            })
1806
 
            .add(getPrimaryView().makeSearchIndex())
1807
 
            .add(new CollectionSearchIndex() {// for computers
1808
 
                protected Computer get(String key) { return getComputer(key); }
1809
 
                protected Collection<Computer> all() { return computers.values(); }
1810
 
            })
1811
 
            .add(new CollectionSearchIndex() {// for users
1812
 
                protected User get(String key) { return User.get(key,false); }
1813
 
                protected Collection<User> all() { return User.getAll(); }
1814
 
            })
1815
 
            .add(new CollectionSearchIndex() {// for views
1816
 
                protected View get(String key) { return getView(key); }
1817
 
                protected Collection<View> all() { return views; }
1818
 
            });
1819
 
    }
1820
 
 
1821
 
    public String getUrlChildPrefix() {
1822
 
        return "job";
1823
 
    }
1824
 
 
1825
 
    /**
1826
 
     * Gets the absolute URL of Jenkins,
1827
 
     * such as "http://localhost/jenkins/".
1828
 
     *
1829
 
     * <p>
1830
 
     * This method first tries to use the manually configured value, then
1831
 
     * fall back to {@link StaplerRequest#getRootPath()}.
1832
 
     * It is done in this order so that it can work correctly even in the face
1833
 
     * of a reverse proxy.
1834
 
     *
1835
 
     * @return
1836
 
     *      This method returns null if this parameter is not configured by the user.
1837
 
     *      The caller must gracefully deal with this situation.
1838
 
     *      The returned URL will always have the trailing '/'.
1839
 
     * @since 1.66
1840
 
     * @see Descriptor#getCheckUrl(String)
1841
 
     * @see #getRootUrlFromRequest()
1842
 
     */
1843
 
    public String getRootUrl() {
1844
 
        // for compatibility. the actual data is stored in Mailer
1845
 
        String url = Mailer.descriptor().getUrl();
1846
 
        if(url!=null) {
1847
 
            if (!url.endsWith("/")) url += '/';
1848
 
            return url;
1849
 
        }
1850
 
 
1851
 
        StaplerRequest req = Stapler.getCurrentRequest();
1852
 
        if(req!=null)
1853
 
            return getRootUrlFromRequest();
1854
 
        return null;
1855
 
    }
1856
 
 
1857
 
    /**
1858
 
     * Is Jenkins running in HTTPS?
1859
 
     *
1860
 
     * Note that we can't really trust {@link StaplerRequest#isSecure()} because HTTPS might be terminated
1861
 
     * in the reverse proxy.
1862
 
     */
1863
 
    public boolean isRootUrlSecure() {
1864
 
        String url = getRootUrl();
1865
 
        return url!=null && url.startsWith("https");
1866
 
    }
1867
 
 
1868
 
    /**
1869
 
     * Gets the absolute URL of Hudson top page, such as "http://localhost/hudson/".
1870
 
     *
1871
 
     * <p>
1872
 
     * Unlike {@link #getRootUrl()}, which uses the manually configured value,
1873
 
     * this one uses the current request to reconstruct the URL. The benefit is
1874
 
     * that this is immune to the configuration mistake (users often fail to set the root URL
1875
 
     * correctly, especially when a migration is involved), but the downside
1876
 
     * is that unless you are processing a request, this method doesn't work.
1877
 
     *
1878
 
     * Please note that this will not work in all cases if Jenkins is running behind a
1879
 
     * reverse proxy (e.g. when user has switched off ProxyPreserveHost, which is
1880
 
     * default setup or the actual url uses https) and you should use getRootUrl if
1881
 
     * you want to be sure you reflect user setup.
1882
 
     * See https://wiki.jenkins-ci.org/display/JENKINS/Running+Jenkins+behind+Apache
1883
 
     *
1884
 
     * @since 1.263
1885
 
     */
1886
 
    public String getRootUrlFromRequest() {
1887
 
        StaplerRequest req = Stapler.getCurrentRequest();
1888
 
        StringBuilder buf = new StringBuilder();
1889
 
        buf.append(req.getScheme()+"://");
1890
 
        buf.append(req.getServerName());
1891
 
        if(req.getServerPort()!=80)
1892
 
            buf.append(':').append(req.getServerPort());
1893
 
        buf.append(req.getContextPath()).append('/');
1894
 
        return buf.toString();
1895
 
    }
1896
 
 
1897
 
    public File getRootDir() {
1898
 
        return root;
1899
 
    }
1900
 
 
1901
 
    public FilePath getWorkspaceFor(TopLevelItem item) {
1902
 
        return new FilePath(expandVariablesForDirectory(workspaceDir, item));
1903
 
    }
1904
 
 
1905
 
    public File getBuildDirFor(Job job) {
1906
 
        return expandVariablesForDirectory(buildsDir, job);
1907
 
    }
1908
 
 
1909
 
    private File expandVariablesForDirectory(String base, Item item) {
1910
 
        return new File(Util.replaceMacro(base, ImmutableMap.of(
1911
 
                "JENKINS_HOME", getRootDir().getPath(),
1912
 
                "ITEM_ROOTDIR", item.getRootDir().getPath(),
1913
 
                "ITEM_FULLNAME", item.getFullName())));
1914
 
    }
1915
 
    
1916
 
    public String getRawWorkspaceDir() {
1917
 
        return workspaceDir;
1918
 
    }
1919
 
 
1920
 
    public String getRawBuildsDir() {
1921
 
        return buildsDir;
1922
 
    }
1923
 
 
1924
 
    public FilePath getRootPath() {
1925
 
        return new FilePath(getRootDir());
1926
 
    }
1927
 
 
1928
 
    @Override
1929
 
    public FilePath createPath(String absolutePath) {
1930
 
        return new FilePath((VirtualChannel)null,absolutePath);
1931
 
    }
1932
 
 
1933
 
    public ClockDifference getClockDifference() {
1934
 
        return ClockDifference.ZERO;
1935
 
    }
1936
 
 
1937
 
    /**
1938
 
     * For binding {@link LogRecorderManager} to "/log".
1939
 
     * Everything below here is admin-only, so do the check here.
1940
 
     */
1941
 
    public LogRecorderManager getLog() {
1942
 
        checkPermission(ADMINISTER);
1943
 
        return log;
1944
 
    }
1945
 
 
1946
 
    /**
1947
 
     * A convenience method to check if there's some security
1948
 
     * restrictions in place.
1949
 
     */
1950
 
    @Exported
1951
 
    public boolean isUseSecurity() {
1952
 
        return securityRealm!=SecurityRealm.NO_AUTHENTICATION || authorizationStrategy!=AuthorizationStrategy.UNSECURED;
1953
 
    }
1954
 
    
1955
 
    public boolean isUseProjectNamingStrategy(){
1956
 
        return projectNamingStrategy != DefaultProjectNamingStrategy.DEFAULT_NAMING_STRATEGY;
1957
 
    }
1958
 
 
1959
 
    /**
1960
 
     * If true, all the POST requests to Hudson would have to have crumb in it to protect
1961
 
     * Hudson from CSRF vulnerabilities.
1962
 
     */
1963
 
    @Exported
1964
 
    public boolean isUseCrumbs() {
1965
 
        return crumbIssuer!=null;
1966
 
    }
1967
 
 
1968
 
    /**
1969
 
     * Returns the constant that captures the three basic security modes
1970
 
     * in Hudson.
1971
 
     */
1972
 
    public SecurityMode getSecurity() {
1973
 
        // fix the variable so that this code works under concurrent modification to securityRealm.
1974
 
        SecurityRealm realm = securityRealm;
1975
 
 
1976
 
        if(realm==SecurityRealm.NO_AUTHENTICATION)
1977
 
            return SecurityMode.UNSECURED;
1978
 
        if(realm instanceof LegacySecurityRealm)
1979
 
            return SecurityMode.LEGACY;
1980
 
        return SecurityMode.SECURED;
1981
 
    }
1982
 
 
1983
 
    /**
1984
 
     * @return
1985
 
     *      never null.
1986
 
     */
1987
 
    public SecurityRealm getSecurityRealm() {
1988
 
        return securityRealm;
1989
 
    }
1990
 
 
1991
 
    public void setSecurityRealm(SecurityRealm securityRealm) {
1992
 
        if(securityRealm==null)
1993
 
            securityRealm= SecurityRealm.NO_AUTHENTICATION;
1994
 
        this.useSecurity = true;
1995
 
        this.securityRealm = securityRealm;
1996
 
        // reset the filters and proxies for the new SecurityRealm
1997
 
        try {
1998
 
            HudsonFilter filter = HudsonFilter.get(servletContext);
1999
 
            if (filter == null) {
2000
 
                // Fix for #3069: This filter is not necessarily initialized before the servlets.
2001
 
                // when HudsonFilter does come back, it'll initialize itself.
2002
 
                LOGGER.fine("HudsonFilter has not yet been initialized: Can't perform security setup for now");
2003
 
            } else {
2004
 
                LOGGER.fine("HudsonFilter has been previously initialized: Setting security up");
2005
 
                filter.reset(securityRealm);
2006
 
                LOGGER.fine("Security is now fully set up");
2007
 
            }
2008
 
        } catch (ServletException e) {
2009
 
            // for binary compatibility, this method cannot throw a checked exception
2010
 
            throw new AcegiSecurityException("Failed to configure filter",e) {};
2011
 
        }
2012
 
    }
2013
 
 
2014
 
    public void setAuthorizationStrategy(AuthorizationStrategy a) {
2015
 
        if (a == null)
2016
 
            a = AuthorizationStrategy.UNSECURED;
2017
 
        useSecurity = true;
2018
 
        authorizationStrategy = a;
2019
 
    }
2020
 
 
2021
 
    public void disableSecurity() {
2022
 
        useSecurity = null;
2023
 
        setSecurityRealm(SecurityRealm.NO_AUTHENTICATION);
2024
 
        authorizationStrategy = AuthorizationStrategy.UNSECURED;
2025
 
        markupFormatter = null;
2026
 
    }
2027
 
 
2028
 
    public void setProjectNamingStrategy(ProjectNamingStrategy ns) {
2029
 
        if(ns == null){
2030
 
            ns = DefaultProjectNamingStrategy.DEFAULT_NAMING_STRATEGY;
2031
 
        }
2032
 
        projectNamingStrategy = ns;
2033
 
    }
2034
 
    
2035
 
    public Lifecycle getLifecycle() {
2036
 
        return Lifecycle.get();
2037
 
    }
2038
 
 
2039
 
    /**
2040
 
     * Gets the dependency injection container that hosts all the extension implementations and other
2041
 
     * components in Jenkins.
2042
 
     *
2043
 
     * @since 1.GUICE
2044
 
     */
2045
 
    public Injector getInjector() {
2046
 
        return lookup(Injector.class);
2047
 
    }
2048
 
 
2049
 
    /**
2050
 
     * Returns {@link ExtensionList} that retains the discovered instances for the given extension type.
2051
 
     *
2052
 
     * @param extensionType
2053
 
     *      The base type that represents the extension point. Normally {@link ExtensionPoint} subtype
2054
 
     *      but that's not a hard requirement.
2055
 
     * @return
2056
 
     *      Can be an empty list but never null.
2057
 
     */
2058
 
    @SuppressWarnings({"unchecked"})
2059
 
    public <T> ExtensionList<T> getExtensionList(Class<T> extensionType) {
2060
 
        return extensionLists.get(extensionType);
2061
 
    }
2062
 
 
2063
 
    /**
2064
 
     * Used to bind {@link ExtensionList}s to URLs.
2065
 
     *
2066
 
     * @since 1.349
2067
 
     */
2068
 
    public ExtensionList getExtensionList(String extensionType) throws ClassNotFoundException {
2069
 
        return getExtensionList(pluginManager.uberClassLoader.loadClass(extensionType));
2070
 
    }
2071
 
 
2072
 
    /**
2073
 
     * Returns {@link ExtensionList} that retains the discovered {@link Descriptor} instances for the given
2074
 
     * kind of {@link Describable}.
2075
 
     *
2076
 
     * @return
2077
 
     *      Can be an empty list but never null.
2078
 
     */
2079
 
    @SuppressWarnings({"unchecked"})
2080
 
    public <T extends Describable<T>,D extends Descriptor<T>> DescriptorExtensionList<T,D> getDescriptorList(Class<T> type) {
2081
 
        return descriptorLists.get(type);
2082
 
    }
2083
 
 
2084
 
    /**
2085
 
     * Refresh {@link ExtensionList}s by adding all the newly discovered extensions.
2086
 
     *
2087
 
     * Exposed only for {@link PluginManager#dynamicLoad(File)}.
2088
 
     */
2089
 
    public void refreshExtensions() throws ExtensionRefreshException {
2090
 
        ExtensionList<ExtensionFinder> finders = getExtensionList(ExtensionFinder.class);
2091
 
        for (ExtensionFinder ef : finders) {
2092
 
            if (!ef.isRefreshable())
2093
 
                throw new ExtensionRefreshException(ef+" doesn't support refresh");
2094
 
        }
2095
 
 
2096
 
        List<ExtensionComponentSet> fragments = Lists.newArrayList();
2097
 
        for (ExtensionFinder ef : finders) {
2098
 
            fragments.add(ef.refresh());
2099
 
        }
2100
 
        ExtensionComponentSet delta = ExtensionComponentSet.union(fragments).filtered();
2101
 
 
2102
 
        // if we find a new ExtensionFinder, we need it to list up all the extension points as well
2103
 
        List<ExtensionComponent<ExtensionFinder>> newFinders = Lists.newArrayList(delta.find(ExtensionFinder.class));
2104
 
        while (!newFinders.isEmpty()) {
2105
 
            ExtensionFinder f = newFinders.remove(newFinders.size()-1).getInstance();
2106
 
 
2107
 
            ExtensionComponentSet ecs = ExtensionComponentSet.allOf(f).filtered();
2108
 
            newFinders.addAll(ecs.find(ExtensionFinder.class));
2109
 
            delta = ExtensionComponentSet.union(delta, ecs);
2110
 
        }
2111
 
 
2112
 
        for (ExtensionList el : extensionLists.values()) {
2113
 
            el.refresh(delta);
2114
 
        }
2115
 
        for (ExtensionList el : descriptorLists.values()) {
2116
 
            el.refresh(delta);
2117
 
        }
2118
 
 
2119
 
        // TODO: we need some generalization here so that extension points can be notified when a refresh happens?
2120
 
        for (ExtensionComponent<RootAction> ea : delta.find(RootAction.class)) {
2121
 
            Action a = ea.getInstance();
2122
 
            if (!actions.contains(a)) actions.add(a);
2123
 
        }
2124
 
    }
2125
 
 
2126
 
    /**
2127
 
     * Returns the root {@link ACL}.
2128
 
     *
2129
 
     * @see AuthorizationStrategy#getRootACL()
2130
 
     */
2131
 
    @Override
2132
 
    public ACL getACL() {
2133
 
        return authorizationStrategy.getRootACL();
2134
 
    }
2135
 
 
2136
 
    /**
2137
 
     * @return
2138
 
     *      never null.
2139
 
     */
2140
 
    public AuthorizationStrategy getAuthorizationStrategy() {
2141
 
        return authorizationStrategy;
2142
 
    }
2143
 
    
2144
 
    /**
2145
 
     * The strategy used to check the project names.
2146
 
     * @return never <code>null</code>
2147
 
     */
2148
 
    public ProjectNamingStrategy getProjectNamingStrategy() {
2149
 
        return projectNamingStrategy == null ? ProjectNamingStrategy.DEFAULT_NAMING_STRATEGY : projectNamingStrategy;
2150
 
    }
2151
 
 
2152
 
    /**
2153
 
     * Returns true if Hudson is quieting down.
2154
 
     * <p>
2155
 
     * No further jobs will be executed unless it
2156
 
     * can be finished while other current pending builds
2157
 
     * are still in progress.
2158
 
     */
2159
 
    @Exported
2160
 
    public boolean isQuietingDown() {
2161
 
        return isQuietingDown;
2162
 
    }
2163
 
 
2164
 
    /**
2165
 
     * Returns true if the container initiated the termination of the web application.
2166
 
     */
2167
 
    public boolean isTerminating() {
2168
 
        return terminating;
2169
 
    }
2170
 
 
2171
 
    /**
2172
 
     * Gets the initialization milestone that we've already reached.
2173
 
     *
2174
 
     * @return
2175
 
     *      {@link InitMilestone#STARTED} even if the initialization hasn't been started, so that this method
2176
 
     *      never returns null.
2177
 
     */
2178
 
    public InitMilestone getInitLevel() {
2179
 
        return initLevel;
2180
 
    }
2181
 
 
2182
 
    public void setNumExecutors(int n) throws IOException {
2183
 
        this.numExecutors = n;
2184
 
        save();
2185
 
    }
2186
 
 
2187
 
 
2188
 
 
2189
 
    /**
2190
 
     * {@inheritDoc}.
2191
 
     *
2192
 
     * Note that the look up is case-insensitive.
2193
 
     */
2194
 
    public TopLevelItem getItem(String name) {
2195
 
        if (name==null)    return null;
2196
 
        TopLevelItem item = items.get(name);
2197
 
        if (item==null)
2198
 
            return null;
2199
 
        if (!item.hasPermission(Item.READ)) {
2200
 
            if (item.hasPermission(Item.DISCOVER)) {
2201
 
                throw new AccessDeniedException("Please login to access job " + name);
2202
 
            }
2203
 
            return null;
2204
 
        }
2205
 
        return item;
2206
 
    }
2207
 
 
2208
 
    /**
2209
 
     * Gets the item by its path name from the given context
2210
 
     *
2211
 
     * <h2>Path Names</h2>
2212
 
     * <p>
2213
 
     * If the name starts from '/', like "/foo/bar/zot", then it's interpreted as absolute.
2214
 
     * Otherwise, the name should be something like "foo/bar" and it's interpreted like
2215
 
     * relative path name in the file system is, against the given context.
2216
 
     *
2217
 
     * @param context
2218
 
     *      null is interpreted as {@link Jenkins}. Base 'directory' of the interpretation.
2219
 
     * @since 1.406
2220
 
     */
2221
 
    public Item getItem(String pathName, ItemGroup context) {
2222
 
        if (context==null)  context = this;
2223
 
        if (pathName==null) return null;
2224
 
 
2225
 
        if (pathName.startsWith("/"))   // absolute
2226
 
            return getItemByFullName(pathName);
2227
 
 
2228
 
        Object/*Item|ItemGroup*/ ctx = context;
2229
 
 
2230
 
        StringTokenizer tokens = new StringTokenizer(pathName,"/");
2231
 
        while (tokens.hasMoreTokens()) {
2232
 
            String s = tokens.nextToken();
2233
 
            if (s.equals("..")) {
2234
 
                if (ctx instanceof Item) {
2235
 
                    ctx = ((Item)ctx).getParent();
2236
 
                    continue;
2237
 
                }
2238
 
 
2239
 
                ctx=null;    // can't go up further
2240
 
                break;
2241
 
            }
2242
 
            if (s.equals(".")) {
2243
 
                continue;
2244
 
            }
2245
 
 
2246
 
            if (ctx instanceof ItemGroup) {
2247
 
                ItemGroup g = (ItemGroup) ctx;
2248
 
                Item i = g.getItem(s);
2249
 
                if (i==null || !i.hasPermission(Item.READ)) {
2250
 
                    ctx=null;    // can't go up further
2251
 
                    break;
2252
 
                }
2253
 
                ctx=i;
2254
 
            } else {
2255
 
                return null;
2256
 
            }
2257
 
        }
2258
 
 
2259
 
        if (ctx instanceof Item)
2260
 
            return (Item)ctx;
2261
 
 
2262
 
        // fall back to the classic interpretation
2263
 
        return getItemByFullName(pathName);
2264
 
    }
2265
 
 
2266
 
    public final Item getItem(String pathName, Item context) {
2267
 
        return getItem(pathName,context!=null?context.getParent():null);
2268
 
    }
2269
 
 
2270
 
    public final <T extends Item> T getItem(String pathName, ItemGroup context, Class<T> type) {
2271
 
        Item r = getItem(pathName, context);
2272
 
        if (type.isInstance(r))
2273
 
            return type.cast(r);
2274
 
        return null;
2275
 
    }
2276
 
 
2277
 
    public final <T extends Item> T getItem(String pathName, Item context, Class<T> type) {
2278
 
        return getItem(pathName,context!=null?context.getParent():null,type);
2279
 
    }
2280
 
 
2281
 
    public File getRootDirFor(TopLevelItem child) {
2282
 
        return getRootDirFor(child.getName());
2283
 
    }
2284
 
 
2285
 
    private File getRootDirFor(String name) {
2286
 
        return new File(new File(getRootDir(),"jobs"), name);
2287
 
    }
2288
 
 
2289
 
    /**
2290
 
     * Gets the {@link Item} object by its full name.
2291
 
     * Full names are like path names, where each name of {@link Item} is
2292
 
     * combined by '/'.
2293
 
     *
2294
 
     * @return
2295
 
     *      null if either such {@link Item} doesn't exist under the given full name,
2296
 
     *      or it exists but it's no an instance of the given type.
2297
 
     */
2298
 
    public @CheckForNull <T extends Item> T getItemByFullName(String fullName, Class<T> type) {
2299
 
        StringTokenizer tokens = new StringTokenizer(fullName,"/");
2300
 
        ItemGroup parent = this;
2301
 
 
2302
 
        if(!tokens.hasMoreTokens()) return null;    // for example, empty full name.
2303
 
 
2304
 
        while(true) {
2305
 
            Item item = parent.getItem(tokens.nextToken());
2306
 
            if(!tokens.hasMoreTokens()) {
2307
 
                if(type.isInstance(item))
2308
 
                    return type.cast(item);
2309
 
                else
2310
 
                    return null;
2311
 
            }
2312
 
 
2313
 
            if(!(item instanceof ItemGroup))
2314
 
                return null;    // this item can't have any children
2315
 
 
2316
 
            if (!item.hasPermission(Item.READ))
2317
 
                return null;
2318
 
 
2319
 
            parent = (ItemGroup) item;
2320
 
        }
2321
 
    }
2322
 
 
2323
 
    public @CheckForNull Item getItemByFullName(String fullName) {
2324
 
        return getItemByFullName(fullName,Item.class);
2325
 
    }
2326
 
 
2327
 
    /**
2328
 
     * Gets the user of the given name.
2329
 
     *
2330
 
     * @return the user of the given name, if that person exists or the invoker {@link #hasPermission} on {@link #ADMINISTER}; else null
2331
 
     * @see User#get(String,boolean)
2332
 
     */
2333
 
    public @CheckForNull User getUser(String name) {
2334
 
        return User.get(name,hasPermission(ADMINISTER));
2335
 
    }
2336
 
 
2337
 
    public synchronized TopLevelItem createProject( TopLevelItemDescriptor type, String name ) throws IOException {
2338
 
        return createProject(type, name, true);
2339
 
    }
2340
 
 
2341
 
    public synchronized TopLevelItem createProject( TopLevelItemDescriptor type, String name, boolean notify ) throws IOException {
2342
 
        return itemGroupMixIn.createProject(type,name,notify);
2343
 
    }
2344
 
 
2345
 
    /**
2346
 
     * Overwrites the existing item by new one.
2347
 
     *
2348
 
     * <p>
2349
 
     * This is a short cut for deleting an existing job and adding a new one.
2350
 
     */
2351
 
    public synchronized void putItem(TopLevelItem item) throws IOException, InterruptedException {
2352
 
        String name = item.getName();
2353
 
        TopLevelItem old = items.get(name);
2354
 
        if (old ==item)  return; // noop
2355
 
 
2356
 
        checkPermission(Item.CREATE);
2357
 
        if (old!=null)
2358
 
            old.delete();
2359
 
        items.put(name,item);
2360
 
        ItemListener.fireOnCreated(item);
2361
 
    }
2362
 
 
2363
 
    /**
2364
 
     * Creates a new job.
2365
 
     *
2366
 
     * <p>
2367
 
     * This version infers the descriptor from the type of the top-level item.
2368
 
     *
2369
 
     * @throws IllegalArgumentException
2370
 
     *      if the project of the given name already exists.
2371
 
     */
2372
 
    public synchronized <T extends TopLevelItem> T createProject( Class<T> type, String name ) throws IOException {
2373
 
        return type.cast(createProject((TopLevelItemDescriptor)getDescriptor(type),name));
2374
 
    }
2375
 
 
2376
 
    /**
2377
 
     * Called by {@link Job#renameTo(String)} to update relevant data structure.
2378
 
     * assumed to be synchronized on Hudson by the caller.
2379
 
     */
2380
 
    public void onRenamed(TopLevelItem job, String oldName, String newName) throws IOException {
2381
 
        items.remove(oldName);
2382
 
        items.put(newName,job);
2383
 
 
2384
 
        for (View v : views)
2385
 
            v.onJobRenamed(job, oldName, newName);
2386
 
        save();
2387
 
    }
2388
 
 
2389
 
    /**
2390
 
     * Called in response to {@link Job#doDoDelete(StaplerRequest, StaplerResponse)}
2391
 
     */
2392
 
    public void onDeleted(TopLevelItem item) throws IOException {
2393
 
        for (ItemListener l : ItemListener.all())
2394
 
            l.onDeleted(item);
2395
 
 
2396
 
        items.remove(item.getName());
2397
 
        for (View v : views)
2398
 
            v.onJobRenamed(item, item.getName(), null);
2399
 
        save();
2400
 
    }
2401
 
 
2402
 
    public FingerprintMap getFingerprintMap() {
2403
 
        return fingerprintMap;
2404
 
    }
2405
 
 
2406
 
    // if no finger print matches, display "not found page".
2407
 
    public Object getFingerprint( String md5sum ) throws IOException {
2408
 
        Fingerprint r = fingerprintMap.get(md5sum);
2409
 
        if(r==null)     return new NoFingerprintMatch(md5sum);
2410
 
        else            return r;
2411
 
    }
2412
 
 
2413
 
    /**
2414
 
     * Gets a {@link Fingerprint} object if it exists.
2415
 
     * Otherwise null.
2416
 
     */
2417
 
    public Fingerprint _getFingerprint( String md5sum ) throws IOException {
2418
 
        return fingerprintMap.get(md5sum);
2419
 
    }
2420
 
 
2421
 
    /**
2422
 
     * The file we save our configuration.
2423
 
     */
2424
 
    private XmlFile getConfigFile() {
2425
 
        return new XmlFile(XSTREAM, new File(root,"config.xml"));
2426
 
    }
2427
 
 
2428
 
    public int getNumExecutors() {
2429
 
        return numExecutors;
2430
 
    }
2431
 
 
2432
 
    public Mode getMode() {
2433
 
        return mode;
2434
 
    }
2435
 
 
2436
 
    public void setMode(Mode m) throws IOException {
2437
 
        this.mode = m;
2438
 
        save();
2439
 
    }
2440
 
 
2441
 
    public String getLabelString() {
2442
 
        return fixNull(label).trim();
2443
 
    }
2444
 
 
2445
 
    @Override
2446
 
    public void setLabelString(String label) throws IOException {
2447
 
        this.label = label;
2448
 
        save();
2449
 
    }
2450
 
 
2451
 
    @Override
2452
 
    public LabelAtom getSelfLabel() {
2453
 
        return getLabelAtom("master");
2454
 
    }
2455
 
 
2456
 
    public Computer createComputer() {
2457
 
        return new Hudson.MasterComputer();
2458
 
    }
2459
 
 
2460
 
    private synchronized TaskBuilder loadTasks() throws IOException {
2461
 
        File projectsDir = new File(root,"jobs");
2462
 
        if(!projectsDir.isDirectory() && !projectsDir.mkdirs()) {
2463
 
            if(projectsDir.exists())
2464
 
                throw new IOException(projectsDir+" is not a directory");
2465
 
            throw new IOException("Unable to create "+projectsDir+"\nPermission issue? Please create this directory manually.");
2466
 
        }
2467
 
        File[] subdirs = projectsDir.listFiles(new FileFilter() {
2468
 
            public boolean accept(File child) {
2469
 
                return child.isDirectory() && Items.getConfigFile(child).exists();
2470
 
            }
2471
 
        });
2472
 
 
2473
 
        TaskGraphBuilder g = new TaskGraphBuilder();
2474
 
        Handle loadHudson = g.requires(EXTENSIONS_AUGMENTED).attains(JOB_LOADED).add("Loading global config", new Executable() {
2475
 
            public void run(Reactor session) throws Exception {
2476
 
                // JENKINS-8043: some slaves (eg. swarm slaves) are not saved into the config file
2477
 
                // and will get overwritten when reloading. Make a backup copy now, and re-add them later
2478
 
                NodeList oldSlaves = slaves;
2479
 
                
2480
 
                XmlFile cfg = getConfigFile();
2481
 
                if (cfg.exists()) {
2482
 
                    // reset some data that may not exist in the disk file
2483
 
                    // so that we can take a proper compensation action later.
2484
 
                    primaryView = null;
2485
 
                    views.clear();
2486
 
 
2487
 
                    // load from disk
2488
 
                    cfg.unmarshal(Jenkins.this);
2489
 
                }
2490
 
 
2491
 
                // if we are loading old data that doesn't have this field
2492
 
                if (slaves == null) slaves = new NodeList();
2493
 
 
2494
 
                clouds.setOwner(Jenkins.this);
2495
 
                items.clear();
2496
 
 
2497
 
                // JENKINS-8043: re-add the slaves which were not saved into the config file
2498
 
                // and are now missing, but still connected.
2499
 
                if (oldSlaves != null) {
2500
 
                    ArrayList<Node> newSlaves = new ArrayList<Node>(slaves);
2501
 
                    for (Node n: oldSlaves) {
2502
 
                        if (n instanceof EphemeralNode) {
2503
 
                            if(!newSlaves.contains(n)) {
2504
 
                                newSlaves.add(n);
2505
 
                            }
2506
 
                        }
2507
 
                    }
2508
 
                    setNodes(newSlaves);
2509
 
                }
2510
 
            }
2511
 
        });
2512
 
 
2513
 
        for (final File subdir : subdirs) {
2514
 
            g.requires(loadHudson).attains(JOB_LOADED).notFatal().add("Loading job "+subdir.getName(),new Executable() {
2515
 
                public void run(Reactor session) throws Exception {
2516
 
                    TopLevelItem item = (TopLevelItem) Items.load(Jenkins.this, subdir);
2517
 
                    items.put(item.getName(), item);
2518
 
                }
2519
 
            });
2520
 
        }
2521
 
 
2522
 
        g.requires(JOB_LOADED).add("Finalizing set up",new Executable() {
2523
 
            public void run(Reactor session) throws Exception {
2524
 
                rebuildDependencyGraph();
2525
 
 
2526
 
                {// recompute label objects - populates the labels mapping.
2527
 
                    for (Node slave : slaves)
2528
 
                        // Note that not all labels are visible until the slaves have connected.
2529
 
                        slave.getAssignedLabels();
2530
 
                    getAssignedLabels();
2531
 
                }
2532
 
 
2533
 
                // initialize views by inserting the default view if necessary
2534
 
                // this is both for clean Hudson and for backward compatibility.
2535
 
                if(views.size()==0 || primaryView==null) {
2536
 
                    View v = new AllView(Messages.Hudson_ViewName());
2537
 
                    setViewOwner(v);
2538
 
                    views.add(0,v);
2539
 
                    primaryView = v.getViewName();
2540
 
                }
2541
 
 
2542
 
                // read in old data that doesn't have the security field set
2543
 
                if(authorizationStrategy==null) {
2544
 
                    if(useSecurity==null || !useSecurity)
2545
 
                        authorizationStrategy = AuthorizationStrategy.UNSECURED;
2546
 
                    else
2547
 
                        authorizationStrategy = new LegacyAuthorizationStrategy();
2548
 
                }
2549
 
                if(securityRealm==null) {
2550
 
                    if(useSecurity==null || !useSecurity)
2551
 
                        setSecurityRealm(SecurityRealm.NO_AUTHENTICATION);
2552
 
                    else
2553
 
                        setSecurityRealm(new LegacySecurityRealm());
2554
 
                } else {
2555
 
                    // force the set to proxy
2556
 
                    setSecurityRealm(securityRealm);
2557
 
                }
2558
 
 
2559
 
                if(useSecurity!=null && !useSecurity) {
2560
 
                    // forced reset to the unsecure mode.
2561
 
                    // this works as an escape hatch for people who locked themselves out.
2562
 
                    authorizationStrategy = AuthorizationStrategy.UNSECURED;
2563
 
                    setSecurityRealm(SecurityRealm.NO_AUTHENTICATION);
2564
 
                }
2565
 
 
2566
 
                // Initialize the filter with the crumb issuer
2567
 
                setCrumbIssuer(crumbIssuer);
2568
 
 
2569
 
                // auto register root actions
2570
 
                for (Action a : getExtensionList(RootAction.class))
2571
 
                    if (!actions.contains(a)) actions.add(a);
2572
 
            }
2573
 
        });
2574
 
 
2575
 
        return g;
2576
 
    }
2577
 
 
2578
 
    /**
2579
 
     * Save the settings to a file.
2580
 
     */
2581
 
    public synchronized void save() throws IOException {
2582
 
        if(BulkChange.contains(this))   return;
2583
 
        getConfigFile().write(this);
2584
 
        SaveableListener.fireOnChange(this, getConfigFile());
2585
 
    }
2586
 
 
2587
 
 
2588
 
    /**
2589
 
     * Called to shut down the system.
2590
 
     */
2591
 
    public void cleanUp() {
2592
 
        for (ItemListener l : ItemListener.all())
2593
 
            l.onBeforeShutdown();
2594
 
 
2595
 
        Set<Future<?>> pending = new HashSet<Future<?>>();
2596
 
        terminating = true;
2597
 
        for( Computer c : computers.values() ) {
2598
 
            c.interrupt();
2599
 
            killComputer(c);
2600
 
            pending.add(c.disconnect(null));
2601
 
        }
2602
 
        if(udpBroadcastThread!=null)
2603
 
            udpBroadcastThread.shutdown();
2604
 
        if(dnsMultiCast!=null)
2605
 
            dnsMultiCast.close();
2606
 
        interruptReloadThread();
2607
 
        Trigger.timer.cancel();
2608
 
        // TODO: how to wait for the completion of the last job?
2609
 
        Trigger.timer = null;
2610
 
        if(tcpSlaveAgentListener!=null)
2611
 
            tcpSlaveAgentListener.shutdown();
2612
 
 
2613
 
        if(pluginManager!=null) // be defensive. there could be some ugly timing related issues
2614
 
            pluginManager.stop();
2615
 
 
2616
 
        if(getRootDir().exists())
2617
 
            // if we are aborting because we failed to create JENKINS_HOME,
2618
 
            // don't try to save. Issue #536
2619
 
            getQueue().save();
2620
 
 
2621
 
        threadPoolForLoad.shutdown();
2622
 
        for (Future<?> f : pending)
2623
 
            try {
2624
 
                f.get(10, TimeUnit.SECONDS);    // if clean up operation didn't complete in time, we fail the test
2625
 
            } catch (InterruptedException e) {
2626
 
                Thread.currentThread().interrupt();
2627
 
                break;  // someone wants us to die now. quick!
2628
 
            } catch (ExecutionException e) {
2629
 
                LOGGER.log(Level.WARNING, "Failed to shut down properly",e);
2630
 
            } catch (TimeoutException e) {
2631
 
                LOGGER.log(Level.WARNING, "Failed to shut down properly",e);
2632
 
            }
2633
 
 
2634
 
        LogFactory.releaseAll();
2635
 
 
2636
 
        theInstance = null;
2637
 
    }
2638
 
 
2639
 
    public Object getDynamic(String token) {
2640
 
        for (Action a : getActions()) {
2641
 
            String url = a.getUrlName();
2642
 
            if (url==null)  continue;
2643
 
            if (url.equals(token) || url.equals('/' + token))
2644
 
                return a;
2645
 
        }
2646
 
        for (Action a : getManagementLinks())
2647
 
            if(a.getUrlName().equals(token))
2648
 
                return a;
2649
 
        return null;
2650
 
    }
2651
 
 
2652
 
 
2653
 
//
2654
 
//
2655
 
// actions
2656
 
//
2657
 
//
2658
 
    /**
2659
 
     * Accepts submission from the configuration page.
2660
 
     */
2661
 
    public synchronized void doConfigSubmit( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, FormException {
2662
 
        BulkChange bc = new BulkChange(this);
2663
 
        try {
2664
 
            checkPermission(ADMINISTER);
2665
 
 
2666
 
            JSONObject json = req.getSubmittedForm();
2667
 
 
2668
 
            workspaceDir = json.getString("rawWorkspaceDir");
2669
 
            buildsDir = json.getString("rawBuildsDir");
2670
 
 
2671
 
            systemMessage = Util.nullify(req.getParameter("system_message"));
2672
 
 
2673
 
            jdks.clear();
2674
 
            jdks.addAll(req.bindJSONToList(JDK.class,json.get("jdks")));
2675
 
 
2676
 
            boolean result = true;
2677
 
            for( Descriptor<?> d : Functions.getSortedDescriptorsForGlobalConfig() )
2678
 
                result &= configureDescriptor(req,json,d);
2679
 
 
2680
 
            version = VERSION;
2681
 
 
2682
 
            save();
2683
 
            updateComputerList();
2684
 
            if(result)
2685
 
                FormApply.success(req.getContextPath()+'/').generateResponse(req, rsp, null);
2686
 
            else
2687
 
                FormApply.success("configure").generateResponse(req, rsp, null);    // back to config
2688
 
        } finally {
2689
 
            bc.commit();
2690
 
        }
2691
 
    }
2692
 
 
2693
 
    /**
2694
 
     * Gets the {@link CrumbIssuer} currently in use.
2695
 
     *
2696
 
     * @return null if none is in use.
2697
 
     */
2698
 
    public CrumbIssuer getCrumbIssuer() {
2699
 
        return crumbIssuer;
2700
 
    }
2701
 
 
2702
 
    public void setCrumbIssuer(CrumbIssuer issuer) {
2703
 
        crumbIssuer = issuer;
2704
 
    }
2705
 
 
2706
 
    public synchronized void doTestPost( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
2707
 
        rsp.sendRedirect("foo");
2708
 
    }
2709
 
 
2710
 
    private boolean configureDescriptor(StaplerRequest req, JSONObject json, Descriptor<?> d) throws FormException {
2711
 
        // collapse the structure to remain backward compatible with the JSON structure before 1.
2712
 
        String name = d.getJsonSafeClassName();
2713
 
        JSONObject js = json.has(name) ? json.getJSONObject(name) : new JSONObject(); // if it doesn't have the property, the method returns invalid null object.
2714
 
        json.putAll(js);
2715
 
        return d.configure(req, js);
2716
 
    }
2717
 
 
2718
 
    /**
2719
 
     * Accepts submission from the node configuration page.
2720
 
     */
2721
 
    public synchronized void doConfigExecutorsSubmit( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, FormException {
2722
 
        checkPermission(ADMINISTER);
2723
 
 
2724
 
        BulkChange bc = new BulkChange(this);
2725
 
        try {
2726
 
            JSONObject json = req.getSubmittedForm();
2727
 
 
2728
 
            MasterBuildConfiguration mbc = MasterBuildConfiguration.all().get(MasterBuildConfiguration.class);
2729
 
            if (mbc!=null)
2730
 
                mbc.configure(req,json);
2731
 
 
2732
 
            getNodeProperties().rebuild(req, json.optJSONObject("nodeProperties"), NodeProperty.all());
2733
 
        } finally {
2734
 
            bc.commit();
2735
 
        }
2736
 
 
2737
 
        rsp.sendRedirect(req.getContextPath()+'/'+toComputer().getUrl());  // back to the computer page
2738
 
    }
2739
 
 
2740
 
    /**
2741
 
     * Accepts the new description.
2742
 
     */
2743
 
    public synchronized void doSubmitDescription( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
2744
 
        getPrimaryView().doSubmitDescription(req, rsp);
2745
 
    }
2746
 
 
2747
 
    public synchronized HttpRedirect doQuietDown() throws IOException {
2748
 
        try {
2749
 
            return doQuietDown(false,0);
2750
 
        } catch (InterruptedException e) {
2751
 
            throw new AssertionError(); // impossible
2752
 
        }
2753
 
    }
2754
 
 
2755
 
    @CLIMethod(name="quiet-down")
2756
 
    public HttpRedirect doQuietDown(
2757
 
            @Option(name="-block",usage="Block until the system really quiets down and no builds are running") @QueryParameter boolean block,
2758
 
            @Option(name="-timeout",usage="If non-zero, only block up to the specified number of milliseconds") @QueryParameter int timeout) throws InterruptedException, IOException {
2759
 
        synchronized (this) {
2760
 
            checkPermission(ADMINISTER);
2761
 
            isQuietingDown = true;
2762
 
        }
2763
 
        if (block) {
2764
 
            if (timeout > 0) timeout += System.currentTimeMillis();
2765
 
            while (isQuietingDown
2766
 
                   && (timeout <= 0 || System.currentTimeMillis() < timeout)
2767
 
                   && !RestartListener.isAllReady()) {
2768
 
                Thread.sleep(1000);
2769
 
            }
2770
 
        }
2771
 
        return new HttpRedirect(".");
2772
 
    }
2773
 
 
2774
 
    @CLIMethod(name="cancel-quiet-down")
2775
 
    public synchronized HttpRedirect doCancelQuietDown() {
2776
 
        checkPermission(ADMINISTER);
2777
 
        isQuietingDown = false;
2778
 
        getQueue().scheduleMaintenance();
2779
 
        return new HttpRedirect(".");
2780
 
    }
2781
 
 
2782
 
    /**
2783
 
     * Backward compatibility. Redirect to the thread dump.
2784
 
     */
2785
 
    public void doClassicThreadDump(StaplerResponse rsp) throws IOException, ServletException {
2786
 
        rsp.sendRedirect2("threadDump");
2787
 
    }
2788
 
 
2789
 
    /**
2790
 
     * Obtains the thread dump of all slaves (including the master.)
2791
 
     *
2792
 
     * <p>
2793
 
     * Since this is for diagnostics, it has a built-in precautionary measure against hang slaves.
2794
 
     */
2795
 
    public Map<String,Map<String,String>> getAllThreadDumps() throws IOException, InterruptedException {
2796
 
        checkPermission(ADMINISTER);
2797
 
 
2798
 
        // issue the requests all at once
2799
 
        Map<String,Future<Map<String,String>>> future = new HashMap<String, Future<Map<String, String>>>();
2800
 
 
2801
 
        for (Computer c : getComputers()) {
2802
 
            future.put(c.getName(), RemotingDiagnostics.getThreadDumpAsync(c.getChannel()));
2803
 
        }
2804
 
                if (toComputer() == null) {
2805
 
                        future.put("master", RemotingDiagnostics.getThreadDumpAsync(MasterComputer.localChannel));
2806
 
                }
2807
 
 
2808
 
        // if the result isn't available in 5 sec, ignore that.
2809
 
        // this is a precaution against hang nodes
2810
 
        long endTime = System.currentTimeMillis() + 5000;
2811
 
 
2812
 
        Map<String,Map<String,String>> r = new HashMap<String, Map<String, String>>();
2813
 
        for (Entry<String, Future<Map<String, String>>> e : future.entrySet()) {
2814
 
            try {
2815
 
                r.put(e.getKey(), e.getValue().get(endTime-System.currentTimeMillis(), TimeUnit.MILLISECONDS));
2816
 
            } catch (Exception x) {
2817
 
                StringWriter sw = new StringWriter();
2818
 
                x.printStackTrace(new PrintWriter(sw,true));
2819
 
                r.put(e.getKey(), Collections.singletonMap("Failed to retrieve thread dump",sw.toString()));
2820
 
            }
2821
 
        }
2822
 
        return r;
2823
 
    }
2824
 
 
2825
 
    public synchronized TopLevelItem doCreateItem( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
2826
 
        return itemGroupMixIn.createTopLevelItem(req, rsp);
2827
 
    }
2828
 
 
2829
 
    /**
2830
 
     * @since 1.319
2831
 
     */
2832
 
    public TopLevelItem createProjectFromXML(String name, InputStream xml) throws IOException {
2833
 
        return itemGroupMixIn.createProjectFromXML(name, xml);
2834
 
    }
2835
 
 
2836
 
 
2837
 
    @SuppressWarnings({"unchecked"})
2838
 
    public <T extends TopLevelItem> T copy(T src, String name) throws IOException {
2839
 
        return itemGroupMixIn.copy(src, name);
2840
 
    }
2841
 
 
2842
 
    // a little more convenient overloading that assumes the caller gives us the right type
2843
 
    // (or else it will fail with ClassCastException)
2844
 
    public <T extends AbstractProject<?,?>> T copy(T src, String name) throws IOException {
2845
 
        return (T)copy((TopLevelItem)src,name);
2846
 
    }
2847
 
 
2848
 
    public synchronized void doCreateView( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, FormException {
2849
 
        checkPermission(View.CREATE);
2850
 
        addView(View.create(req,rsp, this));
2851
 
    }
2852
 
 
2853
 
    /**
2854
 
     * Check if the given name is suitable as a name
2855
 
     * for job, view, etc.
2856
 
     *
2857
 
     * @throws ParseException
2858
 
     *      if the given name is not good
2859
 
     */
2860
 
    public static void checkGoodName(String name) throws Failure {
2861
 
        if(name==null || name.length()==0)
2862
 
            throw new Failure(Messages.Hudson_NoName());
2863
 
 
2864
 
        for( int i=0; i<name.length(); i++ ) {
2865
 
            char ch = name.charAt(i);
2866
 
            if(Character.isISOControl(ch)) {
2867
 
                throw new Failure(Messages.Hudson_ControlCodeNotAllowed(toPrintableName(name)));
2868
 
            }
2869
 
            if("?*/\\%!@#$^&|<>[]:;".indexOf(ch)!=-1)
2870
 
                throw new Failure(Messages.Hudson_UnsafeChar(ch));
2871
 
        }
2872
 
 
2873
 
        // looks good
2874
 
    }
2875
 
 
2876
 
    /**
2877
 
     * Makes sure that the given name is good as a job name.
2878
 
     * @return trimmed name if valid; throws ParseException if not
2879
 
     */
2880
 
    private String checkJobName(String name) throws Failure {
2881
 
        checkGoodName(name);
2882
 
        name = name.trim();
2883
 
        projectNamingStrategy.checkName(name);
2884
 
        if(getItem(name)!=null)
2885
 
            throw new Failure(Messages.Hudson_JobAlreadyExists(name));
2886
 
        // looks good
2887
 
        return name;
2888
 
    }
2889
 
 
2890
 
    private static String toPrintableName(String name) {
2891
 
        StringBuilder printableName = new StringBuilder();
2892
 
        for( int i=0; i<name.length(); i++ ) {
2893
 
            char ch = name.charAt(i);
2894
 
            if(Character.isISOControl(ch))
2895
 
                printableName.append("\\u").append((int)ch).append(';');
2896
 
            else
2897
 
                printableName.append(ch);
2898
 
        }
2899
 
        return printableName.toString();
2900
 
    }
2901
 
 
2902
 
    /**
2903
 
     * Checks if the user was successfully authenticated.
2904
 
     *
2905
 
     * @see BasicAuthenticationFilter
2906
 
     */
2907
 
    public void doSecured( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
2908
 
        if(req.getUserPrincipal()==null) {
2909
 
            // authentication must have failed
2910
 
            rsp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
2911
 
            return;
2912
 
        }
2913
 
 
2914
 
        // the user is now authenticated, so send him back to the target
2915
 
        String path = req.getContextPath()+req.getOriginalRestOfPath();
2916
 
        String q = req.getQueryString();
2917
 
        if(q!=null)
2918
 
            path += '?'+q;
2919
 
 
2920
 
        rsp.sendRedirect2(path);
2921
 
    }
2922
 
 
2923
 
    /**
2924
 
     * Called once the user logs in. Just forward to the top page.
2925
 
     */
2926
 
    public void doLoginEntry( StaplerRequest req, StaplerResponse rsp ) throws IOException {
2927
 
        if(req.getUserPrincipal()==null) {
2928
 
            rsp.sendRedirect2("noPrincipal");
2929
 
            return;
2930
 
        }
2931
 
 
2932
 
        String from = req.getParameter("from");
2933
 
        if(from!=null && from.startsWith("/") && !from.equals("/loginError")) {
2934
 
            rsp.sendRedirect2(from);    // I'm bit uncomfortable letting users redircted to other sites, make sure the URL falls into this domain
2935
 
            return;
2936
 
        }
2937
 
 
2938
 
        String url = AbstractProcessingFilter.obtainFullRequestUrl(req);
2939
 
        if(url!=null) {
2940
 
            // if the login redirect is initiated by Acegi
2941
 
            // this should send the user back to where s/he was from.
2942
 
            rsp.sendRedirect2(url);
2943
 
            return;
2944
 
        }
2945
 
 
2946
 
        rsp.sendRedirect2(".");
2947
 
    }
2948
 
 
2949
 
    /**
2950
 
     * Logs out the user.
2951
 
     */
2952
 
    public void doLogout( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
2953
 
        securityRealm.doLogout(req, rsp);
2954
 
    }
2955
 
 
2956
 
    /**
2957
 
     * Serves jar files for JNLP slave agents.
2958
 
     */
2959
 
    public Slave.JnlpJar getJnlpJars(String fileName) {
2960
 
        return new Slave.JnlpJar(fileName);
2961
 
    }
2962
 
 
2963
 
    public Slave.JnlpJar doJnlpJars(StaplerRequest req) {
2964
 
        return new Slave.JnlpJar(req.getRestOfPath().substring(1));
2965
 
    }
2966
 
 
2967
 
    /**
2968
 
     * Reloads the configuration.
2969
 
     */
2970
 
    @CLIMethod(name="reload-configuration")
2971
 
    public synchronized HttpResponse doReload() throws IOException {
2972
 
        checkPermission(ADMINISTER);
2973
 
 
2974
 
        // engage "loading ..." UI and then run the actual task in a separate thread
2975
 
        servletContext.setAttribute("app", new HudsonIsLoading());
2976
 
 
2977
 
        new Thread("Jenkins config reload thread") {
2978
 
            @Override
2979
 
            public void run() {
2980
 
                try {
2981
 
                    ACL.impersonate(ACL.SYSTEM);
2982
 
                    reload();
2983
 
                } catch (Exception e) {
2984
 
                    LOGGER.log(SEVERE,"Failed to reload Jenkins config",e);
2985
 
                    WebApp.get(servletContext).setApp(new JenkinsReloadFailed(e));
2986
 
                }
2987
 
            }
2988
 
        }.start();
2989
 
 
2990
 
        return HttpResponses.redirectViaContextPath("/");
2991
 
    }
2992
 
 
2993
 
    /**
2994
 
     * Reloads the configuration synchronously.
2995
 
     */
2996
 
    public void reload() throws IOException, InterruptedException, ReactorException {
2997
 
        executeReactor(null, loadTasks());
2998
 
        User.reload();
2999
 
        servletContext.setAttribute("app", this);
3000
 
    }
3001
 
 
3002
 
    /**
3003
 
     * Do a finger-print check.
3004
 
     */
3005
 
    public void doDoFingerprintCheck( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
3006
 
        // Parse the request
3007
 
        MultipartFormDataParser p = new MultipartFormDataParser(req);
3008
 
        if(isUseCrumbs() && !getCrumbIssuer().validateCrumb(req, p)) {
3009
 
            rsp.sendError(HttpServletResponse.SC_FORBIDDEN,"No crumb found");
3010
 
        }
3011
 
        try {
3012
 
            rsp.sendRedirect2(req.getContextPath()+"/fingerprint/"+
3013
 
                Util.getDigestOf(p.getFileItem("name").getInputStream())+'/');
3014
 
        } finally {
3015
 
            p.cleanUp();
3016
 
        }
3017
 
    }
3018
 
 
3019
 
    /**
3020
 
     * For debugging. Expose URL to perform GC.
3021
 
     */
3022
 
    @edu.umd.cs.findbugs.annotations.SuppressWarnings("DM_GC")
3023
 
    public void doGc(StaplerResponse rsp) throws IOException {
3024
 
        checkPermission(Jenkins.ADMINISTER);
3025
 
        System.gc();
3026
 
        rsp.setStatus(HttpServletResponse.SC_OK);
3027
 
        rsp.setContentType("text/plain");
3028
 
        rsp.getWriter().println("GCed");
3029
 
    }
3030
 
 
3031
 
    /**
3032
 
     * End point that intentionally throws an exception to test the error behaviour.
3033
 
     */
3034
 
    public void doException() {
3035
 
        throw new RuntimeException();
3036
 
    }
3037
 
 
3038
 
    public ContextMenu doContextMenu(StaplerRequest request, StaplerResponse response) throws IOException, JellyException {
3039
 
        ContextMenu menu = new ContextMenu().from(this, request, response);
3040
 
        for (MenuItem i : menu.items) {
3041
 
            if (i.url.equals("/manage")) {
3042
 
                // add "Manage Jenkins" subitems
3043
 
                i.subMenu = new ContextMenu().from(this, request, response, "manage");
3044
 
            }
3045
 
        }
3046
 
        return menu;
3047
 
    }
3048
 
 
3049
 
    /**
3050
 
     * Obtains the heap dump.
3051
 
     */
3052
 
    public HeapDump getHeapDump() throws IOException {
3053
 
        return new HeapDump(this,MasterComputer.localChannel);
3054
 
    }
3055
 
 
3056
 
    /**
3057
 
     * Simulates OutOfMemoryError.
3058
 
     * Useful to make sure OutOfMemoryHeapDump setting.
3059
 
     */
3060
 
    public void doSimulateOutOfMemory() throws IOException {
3061
 
        checkPermission(ADMINISTER);
3062
 
 
3063
 
        System.out.println("Creating artificial OutOfMemoryError situation");
3064
 
        List<Object> args = new ArrayList<Object>();
3065
 
        while (true)
3066
 
            args.add(new byte[1024*1024]);
3067
 
    }
3068
 
 
3069
 
    private transient final Map<UUID,FullDuplexHttpChannel> duplexChannels = new HashMap<UUID, FullDuplexHttpChannel>();
3070
 
 
3071
 
    /**
3072
 
     * Handles HTTP requests for duplex channels for CLI.
3073
 
     */
3074
 
    public void doCli(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, InterruptedException {
3075
 
        if (!"POST".equals(req.getMethod())) {
3076
 
            // for GET request, serve _cli.jelly, assuming this is a browser
3077
 
            checkPermission(READ);
3078
 
            req.getView(this,"_cli.jelly").forward(req,rsp);
3079
 
            return;
3080
 
        }
3081
 
 
3082
 
        // do not require any permission to establish a CLI connection
3083
 
        // the actual authentication for the connecting Channel is done by CLICommand
3084
 
 
3085
 
        UUID uuid = UUID.fromString(req.getHeader("Session"));
3086
 
        rsp.setHeader("Hudson-Duplex",""); // set the header so that the client would know
3087
 
 
3088
 
        FullDuplexHttpChannel server;
3089
 
        if(req.getHeader("Side").equals("download")) {
3090
 
            duplexChannels.put(uuid,server=new FullDuplexHttpChannel(uuid, !hasPermission(ADMINISTER)) {
3091
 
                protected void main(Channel channel) throws IOException, InterruptedException {
3092
 
                    // capture the identity given by the transport, since this can be useful for SecurityRealm.createCliAuthenticator()
3093
 
                    channel.setProperty(CLICommand.TRANSPORT_AUTHENTICATION,getAuthentication());
3094
 
                    channel.setProperty(CliEntryPoint.class.getName(),new CliManagerImpl(channel));
3095
 
                }
3096
 
            });
3097
 
            try {
3098
 
                server.download(req,rsp);
3099
 
            } finally {
3100
 
                duplexChannels.remove(uuid);
3101
 
            }
3102
 
        } else {
3103
 
            duplexChannels.get(uuid).upload(req,rsp);
3104
 
        }
3105
 
    }
3106
 
 
3107
 
    /**
3108
 
     * Binds /userContent/... to $JENKINS_HOME/userContent.
3109
 
     */
3110
 
    public DirectoryBrowserSupport doUserContent() {
3111
 
        return new DirectoryBrowserSupport(this,getRootPath().child("userContent"),"User content","folder.png",true);
3112
 
    }
3113
 
 
3114
 
    /**
3115
 
     * Perform a restart of Hudson, if we can.
3116
 
     *
3117
 
     * This first replaces "app" to {@link HudsonIsRestarting}
3118
 
     */
3119
 
    @CLIMethod(name="restart")
3120
 
    public void doRestart(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, RestartNotSupportedException {
3121
 
        checkPermission(ADMINISTER);
3122
 
        if (req != null && req.getMethod().equals("GET")) {
3123
 
            req.getView(this,"_restart.jelly").forward(req,rsp);
3124
 
            return;
3125
 
        }
3126
 
 
3127
 
        restart();
3128
 
 
3129
 
        if (rsp != null) // null for CLI
3130
 
            rsp.sendRedirect2(".");
3131
 
    }
3132
 
 
3133
 
    /**
3134
 
     * Queues up a restart of Hudson for when there are no builds running, if we can.
3135
 
     *
3136
 
     * This first replaces "app" to {@link HudsonIsRestarting}
3137
 
     *
3138
 
     * @since 1.332
3139
 
     */
3140
 
    @CLIMethod(name="safe-restart")
3141
 
    public HttpResponse doSafeRestart(StaplerRequest req) throws IOException, ServletException, RestartNotSupportedException {
3142
 
        checkPermission(ADMINISTER);
3143
 
        if (req != null && req.getMethod().equals("GET"))
3144
 
            return HttpResponses.forwardToView(this,"_safeRestart.jelly");
3145
 
 
3146
 
        safeRestart();
3147
 
 
3148
 
        return HttpResponses.redirectToDot();
3149
 
    }
3150
 
 
3151
 
    /**
3152
 
     * Performs a restart.
3153
 
     */
3154
 
    public void restart() throws RestartNotSupportedException {
3155
 
        final Lifecycle lifecycle = Lifecycle.get();
3156
 
        lifecycle.verifyRestartable(); // verify that Hudson is restartable
3157
 
        servletContext.setAttribute("app", new HudsonIsRestarting());
3158
 
 
3159
 
        new Thread("restart thread") {
3160
 
            final String exitUser = getAuthentication().getName();
3161
 
            @Override
3162
 
            public void run() {
3163
 
                try {
3164
 
                    ACL.impersonate(ACL.SYSTEM);
3165
 
 
3166
 
                    // give some time for the browser to load the "reloading" page
3167
 
                    Thread.sleep(5000);
3168
 
                    LOGGER.severe(String.format("Restarting VM as requested by %s",exitUser));
3169
 
                    for (RestartListener listener : RestartListener.all())
3170
 
                        listener.onRestart();
3171
 
                    lifecycle.restart();
3172
 
                } catch (InterruptedException e) {
3173
 
                    LOGGER.log(Level.WARNING, "Failed to restart Hudson",e);
3174
 
                } catch (IOException e) {
3175
 
                    LOGGER.log(Level.WARNING, "Failed to restart Hudson",e);
3176
 
                }
3177
 
            }
3178
 
        }.start();
3179
 
    }
3180
 
 
3181
 
    /**
3182
 
     * Queues up a restart to be performed once there are no builds currently running.
3183
 
     * @since 1.332
3184
 
     */
3185
 
    public void safeRestart() throws RestartNotSupportedException {
3186
 
        final Lifecycle lifecycle = Lifecycle.get();
3187
 
        lifecycle.verifyRestartable(); // verify that Hudson is restartable
3188
 
        // Quiet down so that we won't launch new builds.
3189
 
        isQuietingDown = true;
3190
 
 
3191
 
        new Thread("safe-restart thread") {
3192
 
            final String exitUser = getAuthentication().getName();
3193
 
            @Override
3194
 
            public void run() {
3195
 
                try {
3196
 
                    ACL.impersonate(ACL.SYSTEM);
3197
 
 
3198
 
                    // Wait 'til we have no active executors.
3199
 
                    doQuietDown(true, 0);
3200
 
 
3201
 
                    // Make sure isQuietingDown is still true.
3202
 
                    if (isQuietingDown) {
3203
 
                        servletContext.setAttribute("app",new HudsonIsRestarting());
3204
 
                        // give some time for the browser to load the "reloading" page
3205
 
                        LOGGER.info("Restart in 10 seconds");
3206
 
                        Thread.sleep(10000);
3207
 
                        LOGGER.severe(String.format("Restarting VM as requested by %s",exitUser));
3208
 
                        for (RestartListener listener : RestartListener.all())
3209
 
                            listener.onRestart();
3210
 
                        lifecycle.restart();
3211
 
                    } else {
3212
 
                        LOGGER.info("Safe-restart mode cancelled");
3213
 
                    }
3214
 
                } catch (InterruptedException e) {
3215
 
                    LOGGER.log(Level.WARNING, "Failed to restart Hudson",e);
3216
 
                } catch (IOException e) {
3217
 
                    LOGGER.log(Level.WARNING, "Failed to restart Hudson",e);
3218
 
                }
3219
 
            }
3220
 
        }.start();
3221
 
    }
3222
 
 
3223
 
    /**
3224
 
     * Shutdown the system.
3225
 
     * @since 1.161
3226
 
     */
3227
 
    @CLIMethod(name="shutdown")
3228
 
    public void doExit( StaplerRequest req, StaplerResponse rsp ) throws IOException {
3229
 
        checkPermission(ADMINISTER);
3230
 
        LOGGER.severe(String.format("Shutting down VM as requested by %s from %s",
3231
 
                getAuthentication().getName(), req!=null?req.getRemoteAddr():"???"));
3232
 
        if (rsp!=null) {
3233
 
            rsp.setStatus(HttpServletResponse.SC_OK);
3234
 
            rsp.setContentType("text/plain");
3235
 
            PrintWriter w = rsp.getWriter();
3236
 
            w.println("Shutting down");
3237
 
            w.close();
3238
 
        }
3239
 
 
3240
 
        System.exit(0);
3241
 
    }
3242
 
 
3243
 
 
3244
 
    /**
3245
 
     * Shutdown the system safely.
3246
 
     * @since 1.332
3247
 
     */
3248
 
    @CLIMethod(name="safe-shutdown")
3249
 
    public HttpResponse doSafeExit(StaplerRequest req) throws IOException {
3250
 
        checkPermission(ADMINISTER);
3251
 
        isQuietingDown = true;
3252
 
        final String exitUser = getAuthentication().getName();
3253
 
        final String exitAddr = req!=null ? req.getRemoteAddr() : "unknown";
3254
 
        new Thread("safe-exit thread") {
3255
 
            @Override
3256
 
            public void run() {
3257
 
                try {
3258
 
                    ACL.impersonate(ACL.SYSTEM);
3259
 
                    LOGGER.severe(String.format("Shutting down VM as requested by %s from %s",
3260
 
                                                exitUser, exitAddr));
3261
 
                    // Wait 'til we have no active executors.
3262
 
                    while (isQuietingDown
3263
 
                           && (overallLoad.computeTotalExecutors() > overallLoad.computeIdleExecutors())) {
3264
 
                        Thread.sleep(5000);
3265
 
                    }
3266
 
                    // Make sure isQuietingDown is still true.
3267
 
                    if (isQuietingDown) {
3268
 
                        cleanUp();
3269
 
                        System.exit(0);
3270
 
                    }
3271
 
                } catch (InterruptedException e) {
3272
 
                    LOGGER.log(Level.WARNING, "Failed to shutdown Hudson",e);
3273
 
                }
3274
 
            }
3275
 
        }.start();
3276
 
 
3277
 
        return HttpResponses.plainText("Shutting down as soon as all jobs are complete");
3278
 
    }
3279
 
 
3280
 
    /**
3281
 
     * Gets the {@link Authentication} object that represents the user
3282
 
     * associated with the current request.
3283
 
     */
3284
 
    public static Authentication getAuthentication() {
3285
 
        Authentication a = SecurityContextHolder.getContext().getAuthentication();
3286
 
        // on Tomcat while serving the login page, this is null despite the fact
3287
 
        // that we have filters. Looking at the stack trace, Tomcat doesn't seem to
3288
 
        // run the request through filters when this is the login request.
3289
 
        // see http://www.nabble.com/Matrix-authorization-problem-tp14602081p14886312.html
3290
 
        if(a==null)
3291
 
            a = ANONYMOUS;
3292
 
        return a;
3293
 
    }
3294
 
 
3295
 
    /**
3296
 
     * For system diagnostics.
3297
 
     * Run arbitrary Groovy script.
3298
 
     */
3299
 
    public void doScript(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
3300
 
        doScript(req, rsp, req.getView(this, "_script.jelly"));
3301
 
    }
3302
 
 
3303
 
    /**
3304
 
     * Run arbitrary Groovy script and return result as plain text.
3305
 
     */
3306
 
    public void doScriptText(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
3307
 
        doScript(req, rsp, req.getView(this, "_scriptText.jelly"));
3308
 
    }
3309
 
 
3310
 
    private void doScript(StaplerRequest req, StaplerResponse rsp, RequestDispatcher view) throws IOException, ServletException {
3311
 
        // ability to run arbitrary script is dangerous
3312
 
        checkPermission(RUN_SCRIPTS);
3313
 
 
3314
 
        String text = req.getParameter("script");
3315
 
        if (text != null) {
3316
 
            try {
3317
 
                req.setAttribute("output",
3318
 
                        RemotingDiagnostics.executeGroovy(text, MasterComputer.localChannel));
3319
 
            } catch (InterruptedException e) {
3320
 
                throw new ServletException(e);
3321
 
            }
3322
 
        }
3323
 
 
3324
 
        view.forward(req, rsp);
3325
 
    }
3326
 
 
3327
 
    /**
3328
 
     * Evaluates the Jelly script submitted by the client.
3329
 
     *
3330
 
     * This is useful for system administration as well as unit testing.
3331
 
     */
3332
 
    @RequirePOST
3333
 
    public void doEval(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
3334
 
        checkPermission(ADMINISTER);
3335
 
 
3336
 
        try {
3337
 
            MetaClass mc = WebApp.getCurrent().getMetaClass(getClass());
3338
 
            Script script = mc.classLoader.loadTearOff(JellyClassLoaderTearOff.class).createContext().compileScript(new InputSource(req.getReader()));
3339
 
            new JellyRequestDispatcher(this,script).forward(req,rsp);
3340
 
        } catch (JellyException e) {
3341
 
            throw new ServletException(e);
3342
 
        }
3343
 
    }
3344
 
 
3345
 
    /**
3346
 
     * Sign up for the user account.
3347
 
     */
3348
 
    public void doSignup( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
3349
 
        req.getView(getSecurityRealm(), "signup.jelly").forward(req, rsp);
3350
 
    }
3351
 
 
3352
 
    /**
3353
 
     * Changes the icon size by changing the cookie
3354
 
     */
3355
 
    public void doIconSize( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
3356
 
        String qs = req.getQueryString();
3357
 
        if(qs==null || !ICON_SIZE.matcher(qs).matches())
3358
 
            throw new ServletException();
3359
 
        Cookie cookie = new Cookie("iconSize", qs);
3360
 
        cookie.setMaxAge(/* ~4 mo. */9999999); // #762
3361
 
        rsp.addCookie(cookie);
3362
 
        String ref = req.getHeader("Referer");
3363
 
        if(ref==null)   ref=".";
3364
 
        rsp.sendRedirect2(ref);
3365
 
    }
3366
 
 
3367
 
    public void doFingerprintCleanup(StaplerResponse rsp) throws IOException {
3368
 
        FingerprintCleanupThread.invoke();
3369
 
        rsp.setStatus(HttpServletResponse.SC_OK);
3370
 
        rsp.setContentType("text/plain");
3371
 
        rsp.getWriter().println("Invoked");
3372
 
    }
3373
 
 
3374
 
    public void doWorkspaceCleanup(StaplerResponse rsp) throws IOException {
3375
 
        WorkspaceCleanupThread.invoke();
3376
 
        rsp.setStatus(HttpServletResponse.SC_OK);
3377
 
        rsp.setContentType("text/plain");
3378
 
        rsp.getWriter().println("Invoked");
3379
 
    }
3380
 
 
3381
 
    /**
3382
 
     * If the user chose the default JDK, make sure we got 'java' in PATH.
3383
 
     */
3384
 
    public FormValidation doDefaultJDKCheck(StaplerRequest request, @QueryParameter String value) {
3385
 
        if(!value.equals("(Default)"))
3386
 
            // assume the user configured named ones properly in system config ---
3387
 
            // or else system config should have reported form field validation errors.
3388
 
            return FormValidation.ok();
3389
 
 
3390
 
        // default JDK selected. Does such java really exist?
3391
 
        if(JDK.isDefaultJDKValid(Jenkins.this))
3392
 
            return FormValidation.ok();
3393
 
        else
3394
 
            return FormValidation.errorWithMarkup(Messages.Hudson_NoJavaInPath(request.getContextPath()));
3395
 
    }
3396
 
 
3397
 
    /**
3398
 
     * Makes sure that the given name is good as a job name.
3399
 
     */
3400
 
    public FormValidation doCheckJobName(@QueryParameter String value) {
3401
 
        // this method can be used to check if a file exists anywhere in the file system,
3402
 
        // so it should be protected.
3403
 
        checkPermission(Item.CREATE);
3404
 
 
3405
 
        if(fixEmpty(value)==null)
3406
 
            return FormValidation.ok();
3407
 
 
3408
 
        try {
3409
 
            checkJobName(value);
3410
 
            return FormValidation.ok();
3411
 
        } catch (Failure e) {
3412
 
            return FormValidation.error(e.getMessage());
3413
 
        }
3414
 
    }
3415
 
 
3416
 
    /**
3417
 
     * Checks if a top-level view with the given name exists.
3418
 
     */
3419
 
    public FormValidation doViewExistsCheck(@QueryParameter String value) {
3420
 
        checkPermission(View.CREATE);
3421
 
 
3422
 
        String view = fixEmpty(value);
3423
 
        if(view==null) return FormValidation.ok();
3424
 
 
3425
 
        if(getView(view)==null)
3426
 
            return FormValidation.ok();
3427
 
        else
3428
 
            return FormValidation.error(Messages.Hudson_ViewAlreadyExists(view));
3429
 
    }
3430
 
 
3431
 
    /**
3432
 
     * Serves static resources placed along with Jelly view files.
3433
 
     * <p>
3434
 
     * This method can serve a lot of files, so care needs to be taken
3435
 
     * to make this method secure. It's not clear to me what's the best
3436
 
     * strategy here, though the current implementation is based on
3437
 
     * file extensions.
3438
 
     */
3439
 
    public void doResources(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
3440
 
        String path = req.getRestOfPath();
3441
 
        // cut off the "..." portion of /resources/.../path/to/file
3442
 
        // as this is only used to make path unique (which in turn
3443
 
        // allows us to set a long expiration date
3444
 
        path = path.substring(path.indexOf('/',1)+1);
3445
 
 
3446
 
        int idx = path.lastIndexOf('.');
3447
 
        String extension = path.substring(idx+1);
3448
 
        if(ALLOWED_RESOURCE_EXTENSIONS.contains(extension)) {
3449
 
            URL url = pluginManager.uberClassLoader.getResource(path);
3450
 
            if(url!=null) {
3451
 
                long expires = MetaClass.NO_CACHE ? 0 : 365L * 24 * 60 * 60 * 1000; /*1 year*/
3452
 
                rsp.serveFile(req,url,expires);
3453
 
                return;
3454
 
            }
3455
 
        }
3456
 
        rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
3457
 
    }
3458
 
 
3459
 
    /**
3460
 
     * Extension list that {@link #doResources(StaplerRequest, StaplerResponse)} can serve.
3461
 
     * This set is mutable to allow plugins to add additional extensions.
3462
 
     */
3463
 
    public static final Set<String> ALLOWED_RESOURCE_EXTENSIONS = new HashSet<String>(Arrays.asList(
3464
 
        "js|css|jpeg|jpg|png|gif|html|htm".split("\\|")
3465
 
    ));
3466
 
 
3467
 
    /**
3468
 
     * Checks if container uses UTF-8 to decode URLs. See
3469
 
     * http://wiki.jenkins-ci.org/display/JENKINS/Tomcat#Tomcat-i18n
3470
 
     */
3471
 
    public FormValidation doCheckURIEncoding(StaplerRequest request) throws IOException {
3472
 
        // expected is non-ASCII String
3473
 
        final String expected = "\u57f7\u4e8b";
3474
 
        final String value = fixEmpty(request.getParameter("value"));
3475
 
        if (!expected.equals(value))
3476
 
            return FormValidation.warningWithMarkup(Messages.Hudson_NotUsesUTF8ToDecodeURL());
3477
 
        return FormValidation.ok();
3478
 
    }
3479
 
 
3480
 
    /**
3481
 
     * Does not check when system default encoding is "ISO-8859-1".
3482
 
     */
3483
 
    public static boolean isCheckURIEncodingEnabled() {
3484
 
        return !"ISO-8859-1".equalsIgnoreCase(System.getProperty("file.encoding"));
3485
 
    }
3486
 
 
3487
 
    /**
3488
 
     * Rebuilds the dependency map.
3489
 
     */
3490
 
    public void rebuildDependencyGraph() {
3491
 
        DependencyGraph graph = new DependencyGraph();
3492
 
        graph.build();
3493
 
        // volatile acts a as a memory barrier here and therefore guarantees 
3494
 
        // that graph is fully build, before it's visible to other threads
3495
 
        dependencyGraph = graph;
3496
 
    }
3497
 
 
3498
 
    public DependencyGraph getDependencyGraph() {
3499
 
        return dependencyGraph;
3500
 
    }
3501
 
 
3502
 
    // for Jelly
3503
 
    public List<ManagementLink> getManagementLinks() {
3504
 
        return ManagementLink.all();
3505
 
    }
3506
 
 
3507
 
    /**
3508
 
     * Exposes the current user to <tt>/me</tt> URL.
3509
 
     */
3510
 
    public User getMe() {
3511
 
        User u = User.current();
3512
 
        if (u == null)
3513
 
            throw new AccessDeniedException("/me is not available when not logged in");
3514
 
        return u;
3515
 
    }
3516
 
 
3517
 
    /**
3518
 
     * Gets the {@link Widget}s registered on this object.
3519
 
     *
3520
 
     * <p>
3521
 
     * Plugins who wish to contribute boxes on the side panel can add widgets
3522
 
     * by {@code getWidgets().add(new MyWidget())} from {@link Plugin#start()}.
3523
 
     */
3524
 
    public List<Widget> getWidgets() {
3525
 
        return widgets;
3526
 
    }
3527
 
 
3528
 
    public Object getTarget() {
3529
 
        try {
3530
 
            checkPermission(READ);
3531
 
        } catch (AccessDeniedException e) {
3532
 
            String rest = Stapler.getCurrentRequest().getRestOfPath();
3533
 
            if(rest.startsWith("/login")
3534
 
            || rest.startsWith("/logout")
3535
 
            || rest.startsWith("/accessDenied")
3536
 
            || rest.startsWith("/adjuncts/")
3537
 
            || rest.startsWith("/signup")
3538
 
            || rest.startsWith("/jnlpJars/")
3539
 
            || rest.startsWith("/tcpSlaveAgentListener")
3540
 
            // XXX SlaveComputer.doSlaveAgentJnlp; there should be an annotation to request unprotected access
3541
 
            || rest.matches("/computer/[^/]+/slave-agent[.]jnlp") && "true".equals(Stapler.getCurrentRequest().getParameter("encrypt"))
3542
 
            || rest.startsWith("/cli")
3543
 
            || rest.startsWith("/whoAmI")
3544
 
            || rest.startsWith("/federatedLoginService/")
3545
 
            || rest.startsWith("/securityRealm"))
3546
 
                return this;    // URLs that are always visible without READ permission
3547
 
 
3548
 
            for (Action a : getActions()) {
3549
 
                if (a instanceof UnprotectedRootAction) {
3550
 
                    if (rest.startsWith("/"+a.getUrlName()+"/") || rest.equals("/"+a.getUrlName()))
3551
 
                        return this;
3552
 
                }
3553
 
            }
3554
 
 
3555
 
            throw e;
3556
 
        }
3557
 
        return this;
3558
 
    }
3559
 
 
3560
 
    /**
3561
 
     * Fallback to the primary view.
3562
 
     */
3563
 
    public View getStaplerFallback() {
3564
 
        return getPrimaryView();
3565
 
    }
3566
 
 
3567
 
    /**
3568
 
     * This method checks all existing jobs to see if displayName is 
3569
 
     * unique. It does not check the displayName against the displayName of the
3570
 
     * job that the user is configuring though to prevent a validation warning 
3571
 
     * if the user sets the displayName to what it currently is.
3572
 
     * @param displayName
3573
 
     * @param currentJobName
3574
 
     * @return
3575
 
     */
3576
 
    boolean isDisplayNameUnique(String displayName, String currentJobName) {
3577
 
        Collection<TopLevelItem> itemCollection = items.values();
3578
 
        
3579
 
        // if there are a lot of projects, we'll have to store their 
3580
 
        // display names in a HashSet or something for a quick check
3581
 
        for(TopLevelItem item : itemCollection) {
3582
 
            if(item.getName().equals(currentJobName)) {
3583
 
                // we won't compare the candidate displayName against the current
3584
 
                // item. This is to prevent an validation warning if the user 
3585
 
                // sets the displayName to what the existing display name is
3586
 
                continue;
3587
 
            }
3588
 
            else if(displayName.equals(item.getDisplayName())) {
3589
 
                return false;
3590
 
            }
3591
 
        }
3592
 
        
3593
 
        return true;
3594
 
    }
3595
 
    
3596
 
    /**
3597
 
     * True if there is no item in Jenkins that has this name
3598
 
     * @param name The name to test
3599
 
     * @param currentJobName The name of the job that the user is configuring
3600
 
     * @return
3601
 
     */
3602
 
    boolean isNameUnique(String name, String currentJobName) {
3603
 
        Item item = getItem(name);
3604
 
        
3605
 
        if(null==item) {
3606
 
            // the candidate name didn't return any items so the name is unique
3607
 
            return true;
3608
 
        }
3609
 
        else if(item.getName().equals(currentJobName)) {
3610
 
            // the candidate name returned an item, but the item is the item
3611
 
            // that the user is configuring so this is ok
3612
 
            return true;
3613
 
        } 
3614
 
        else {
3615
 
            // the candidate name returned an item, so it is not unique
3616
 
            return false;
3617
 
        }
3618
 
    }
3619
 
    
3620
 
    /**
3621
 
     * Checks to see if the candidate displayName collides with any 
3622
 
     * existing display names or project names
3623
 
     * @param displayName The display name to test
3624
 
     * @param jobName The name of the job the user is configuring
3625
 
     * @return
3626
 
     */
3627
 
    public FormValidation doCheckDisplayName(@QueryParameter String displayName, 
3628
 
            @QueryParameter String jobName) {
3629
 
        displayName = displayName.trim();
3630
 
        
3631
 
        if(LOGGER.isLoggable(Level.FINE)) {
3632
 
            LOGGER.log(Level.FINE, "Current job name is " + jobName);
3633
 
        }
3634
 
        
3635
 
        if(!isNameUnique(displayName, jobName)) {
3636
 
            return FormValidation.warning(Messages.Jenkins_CheckDisplayName_NameNotUniqueWarning(displayName));
3637
 
        }
3638
 
        else if(!isDisplayNameUnique(displayName, jobName)){
3639
 
            return FormValidation.warning(Messages.Jenkins_CheckDisplayName_DisplayNameNotUniqueWarning(displayName));
3640
 
        }
3641
 
        else {
3642
 
            return FormValidation.ok();
3643
 
        }
3644
 
    }
3645
 
    
3646
 
    public static class MasterComputer extends Computer {
3647
 
        protected MasterComputer() {
3648
 
            super(Jenkins.getInstance());
3649
 
        }
3650
 
 
3651
 
        /**
3652
 
         * Returns "" to match with {@link Jenkins#getNodeName()}.
3653
 
         */
3654
 
        @Override
3655
 
        public String getName() {
3656
 
            return "";
3657
 
        }
3658
 
 
3659
 
        @Override
3660
 
        public boolean isConnecting() {
3661
 
            return false;
3662
 
        }
3663
 
 
3664
 
        @Override
3665
 
        public String getDisplayName() {
3666
 
            return Messages.Hudson_Computer_DisplayName();
3667
 
        }
3668
 
 
3669
 
        @Override
3670
 
        public String getCaption() {
3671
 
            return Messages.Hudson_Computer_Caption();
3672
 
        }
3673
 
 
3674
 
        @Override
3675
 
        public String getUrl() {
3676
 
            return "computer/(master)/";
3677
 
        }
3678
 
 
3679
 
        public RetentionStrategy getRetentionStrategy() {
3680
 
            return RetentionStrategy.NOOP;
3681
 
        }
3682
 
 
3683
 
        /**
3684
 
         * Report an error.
3685
 
         */
3686
 
        @Override
3687
 
        public HttpResponse doDoDelete() throws IOException {
3688
 
            throw HttpResponses.status(SC_BAD_REQUEST);
3689
 
        }
3690
 
 
3691
 
        @Override
3692
 
        public void doConfigSubmit(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, FormException {
3693
 
            Jenkins.getInstance().doConfigExecutorsSubmit(req, rsp);
3694
 
        }
3695
 
 
3696
 
        @Override
3697
 
        public boolean hasPermission(Permission permission) {
3698
 
            // no one should be allowed to delete the master.
3699
 
            // this hides the "delete" link from the /computer/(master) page.
3700
 
            if(permission==Computer.DELETE)
3701
 
                return false;
3702
 
            // Configuration of master node requires ADMINISTER permission
3703
 
            return super.hasPermission(permission==Computer.CONFIGURE ? Jenkins.ADMINISTER : permission);
3704
 
        }
3705
 
 
3706
 
        @Override
3707
 
        public VirtualChannel getChannel() {
3708
 
            return localChannel;
3709
 
        }
3710
 
 
3711
 
        @Override
3712
 
        public Charset getDefaultCharset() {
3713
 
            return Charset.defaultCharset();
3714
 
        }
3715
 
 
3716
 
        public List<LogRecord> getLogRecords() throws IOException, InterruptedException {
3717
 
            return logRecords;
3718
 
        }
3719
 
 
3720
 
        public void doLaunchSlaveAgent(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
3721
 
            // this computer never returns null from channel, so
3722
 
            // this method shall never be invoked.
3723
 
            rsp.sendError(SC_NOT_FOUND);
3724
 
        }
3725
 
 
3726
 
        protected Future<?> _connect(boolean forceReconnect) {
3727
 
            return Futures.precomputed(null);
3728
 
        }
3729
 
 
3730
 
        /**
3731
 
         * {@link LocalChannel} instance that can be used to execute programs locally.
3732
 
         */
3733
 
        public static final LocalChannel localChannel = new LocalChannel(threadPoolForRemoting);
3734
 
    }
3735
 
 
3736
 
    /**
3737
 
     * Shortcut for {@code Hudson.getInstance().lookup.get(type)}
3738
 
     */
3739
 
    public static <T> T lookup(Class<T> type) {
3740
 
        return Jenkins.getInstance().lookup.get(type);
3741
 
    }
3742
 
 
3743
 
    /**
3744
 
     * Live view of recent {@link LogRecord}s produced by Hudson.
3745
 
     */
3746
 
    public static List<LogRecord> logRecords = Collections.emptyList(); // initialized to dummy value to avoid NPE
3747
 
 
3748
 
    /**
3749
 
     * Thread-safe reusable {@link XStream}.
3750
 
     */
3751
 
    public static final XStream XSTREAM = new XStream2();
3752
 
 
3753
 
    /**
3754
 
     * Alias to {@link #XSTREAM} so that one can access additional methods on {@link XStream2} more easily.
3755
 
     */
3756
 
    public static final XStream2 XSTREAM2 = (XStream2)XSTREAM;
3757
 
 
3758
 
    private static final int TWICE_CPU_NUM = Math.max(4, Runtime.getRuntime().availableProcessors() * 2);
3759
 
 
3760
 
    /**
3761
 
     * Thread pool used to load configuration in parallel, to improve the start up time.
3762
 
     * <p>
3763
 
     * The idea here is to overlap the CPU and I/O, so we want more threads than CPU numbers.
3764
 
     */
3765
 
    /*package*/ transient final ExecutorService threadPoolForLoad = new ThreadPoolExecutor(
3766
 
        TWICE_CPU_NUM, TWICE_CPU_NUM,
3767
 
        5L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new DaemonThreadFactory());
3768
 
 
3769
 
 
3770
 
    private static void computeVersion(ServletContext context) {
3771
 
        // set the version
3772
 
        Properties props = new Properties();
3773
 
        try {
3774
 
            InputStream is = Jenkins.class.getResourceAsStream("jenkins-version.properties");
3775
 
            if(is!=null)
3776
 
                props.load(is);
3777
 
        } catch (IOException e) {
3778
 
            e.printStackTrace(); // if the version properties is missing, that's OK.
3779
 
        }
3780
 
        String ver = props.getProperty("version");
3781
 
        if(ver==null)   ver="?";
3782
 
        VERSION = ver;
3783
 
        context.setAttribute("version",ver);
3784
 
 
3785
 
        VERSION_HASH = Util.getDigestOf(ver).substring(0, 8);
3786
 
        SESSION_HASH = Util.getDigestOf(ver+System.currentTimeMillis()).substring(0, 8);
3787
 
 
3788
 
        if(ver.equals("?") || Boolean.getBoolean("hudson.script.noCache"))
3789
 
            RESOURCE_PATH = "";
3790
 
        else
3791
 
            RESOURCE_PATH = "/static/"+SESSION_HASH;
3792
 
 
3793
 
        VIEW_RESOURCE_PATH = "/resources/"+ SESSION_HASH;
3794
 
    }
3795
 
 
3796
 
    /**
3797
 
     * Version number of this Hudson.
3798
 
     */
3799
 
    public static String VERSION="?";
3800
 
 
3801
 
    /**
3802
 
     * Parses {@link #VERSION} into {@link VersionNumber}, or null if it's not parseable as a version number
3803
 
     * (such as when Hudson is run with "mvn hudson-dev:run")
3804
 
     */
3805
 
    public static VersionNumber getVersion() {
3806
 
        try {
3807
 
            return new VersionNumber(VERSION);
3808
 
        } catch (NumberFormatException e) {
3809
 
            try {
3810
 
                // for non-released version of Hudson, this looks like "1.345 (private-foobar), so try to approximate.
3811
 
                int idx = VERSION.indexOf(' ');
3812
 
                if (idx>0)
3813
 
                    return new VersionNumber(VERSION.substring(0,idx));
3814
 
            } catch (NumberFormatException _) {
3815
 
                // fall through
3816
 
            }
3817
 
 
3818
 
            // totally unparseable
3819
 
            return null;
3820
 
        } catch (IllegalArgumentException e) {
3821
 
            // totally unparseable
3822
 
            return null;
3823
 
        }
3824
 
    }
3825
 
 
3826
 
    /**
3827
 
     * Hash of {@link #VERSION}.
3828
 
     */
3829
 
    public static String VERSION_HASH;
3830
 
 
3831
 
    /**
3832
 
     * Unique random token that identifies the current session.
3833
 
     * Used to make {@link #RESOURCE_PATH} unique so that we can set long "Expires" header.
3834
 
     * 
3835
 
     * We used to use {@link #VERSION_HASH}, but making this session local allows us to
3836
 
     * reuse the same {@link #RESOURCE_PATH} for static resources in plugins.
3837
 
     */
3838
 
    public static String SESSION_HASH;
3839
 
 
3840
 
    /**
3841
 
     * Prefix to static resources like images and javascripts in the war file.
3842
 
     * Either "" or strings like "/static/VERSION", which avoids Hudson to pick up
3843
 
     * stale cache when the user upgrades to a different version.
3844
 
     * <p>
3845
 
     * Value computed in {@link WebAppMain}.
3846
 
     */
3847
 
    public static String RESOURCE_PATH = "";
3848
 
 
3849
 
    /**
3850
 
     * Prefix to resources alongside view scripts.
3851
 
     * Strings like "/resources/VERSION", which avoids Hudson to pick up
3852
 
     * stale cache when the user upgrades to a different version.
3853
 
     * <p>
3854
 
     * Value computed in {@link WebAppMain}.
3855
 
     */
3856
 
    public static String VIEW_RESOURCE_PATH = "/resources/TBD";
3857
 
 
3858
 
    public static boolean PARALLEL_LOAD = Configuration.getBooleanConfigParameter("parallelLoad", true);
3859
 
    public static boolean KILL_AFTER_LOAD = Configuration.getBooleanConfigParameter("killAfterLoad", false);
3860
 
    private static final boolean CONSISTENT_HASH = true; // Boolean.getBoolean(Hudson.class.getName()+".consistentHash");
3861
 
    /**
3862
 
     * Enabled by default as of 1.337. Will keep it for a while just in case we have some serious problems.
3863
 
     */
3864
 
    public static boolean FLYWEIGHT_SUPPORT = Configuration.getBooleanConfigParameter("flyweightSupport", true);
3865
 
 
3866
 
    /**
3867
 
     * Tentative switch to activate the concurrent build behavior.
3868
 
     * When we merge this back to the trunk, this allows us to keep
3869
 
     * this feature hidden for a while until we iron out the kinks.
3870
 
     * @see AbstractProject#isConcurrentBuild()
3871
 
     * @deprecated as of 1.464
3872
 
     *      This flag will have no effect.
3873
 
     */
3874
 
    @Restricted(NoExternalUse.class)
3875
 
    public static boolean CONCURRENT_BUILD = true;
3876
 
 
3877
 
    /**
3878
 
     * Switch to enable people to use a shorter workspace name.
3879
 
     */
3880
 
    private static final String WORKSPACE_DIRNAME = Configuration.getStringConfigParameter("workspaceDirName", "workspace");
3881
 
 
3882
 
    /**
3883
 
     * Automatically try to launch a slave when Jenkins is initialized or a new slave is created.
3884
 
     */
3885
 
    public static boolean AUTOMATIC_SLAVE_LAUNCH = true;
3886
 
 
3887
 
    private static final Logger LOGGER = Logger.getLogger(Jenkins.class.getName());
3888
 
 
3889
 
    private static final Pattern ICON_SIZE = Pattern.compile("\\d+x\\d+");
3890
 
 
3891
 
    public static final PermissionGroup PERMISSIONS = Permission.HUDSON_PERMISSIONS;
3892
 
    public static final Permission ADMINISTER = Permission.HUDSON_ADMINISTER;
3893
 
    public static final Permission READ = new Permission(PERMISSIONS,"Read",Messages._Hudson_ReadPermission_Description(),Permission.READ,PermissionScope.JENKINS);
3894
 
    public static final Permission RUN_SCRIPTS = new Permission(PERMISSIONS, "RunScripts", Messages._Hudson_RunScriptsPermission_Description(),ADMINISTER,PermissionScope.JENKINS);
3895
 
 
3896
 
    /**
3897
 
     * {@link Authentication} object that represents the anonymous user.
3898
 
     * Because Acegi creates its own {@link AnonymousAuthenticationToken} instances, the code must not
3899
 
     * expect the singleton semantics. This is just a convenient instance.
3900
 
     *
3901
 
     * @since 1.343
3902
 
     */
3903
 
    public static final Authentication ANONYMOUS = new AnonymousAuthenticationToken(
3904
 
            "anonymous","anonymous",new GrantedAuthority[]{new GrantedAuthorityImpl("anonymous")});
3905
 
 
3906
 
    static {
3907
 
        XSTREAM.alias("jenkins",Jenkins.class);
3908
 
        XSTREAM.alias("slave", DumbSlave.class);
3909
 
        XSTREAM.alias("jdk",JDK.class);
3910
 
        // for backward compatibility with <1.75, recognize the tag name "view" as well.
3911
 
        XSTREAM.alias("view", ListView.class);
3912
 
        XSTREAM.alias("listView", ListView.class);
3913
 
        // this seems to be necessary to force registration of converter early enough
3914
 
        Mode.class.getEnumConstants();
3915
 
 
3916
 
        // double check that initialization order didn't do any harm
3917
 
        assert PERMISSIONS!=null;
3918
 
        assert ADMINISTER!=null;
3919
 
    }
3920
 
 
3921
 
}