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

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Brian Westrich, Red Hat, Inc., Stephen Connolly, Tom Huybrechts
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 package hudson.model;
25
26 import hudson.model.Queue.Executable;
27 import hudson.Util;
28 import hudson.FilePath;
29 import jenkins.model.CauseOfInterruption;
30 import jenkins.model.CauseOfInterruption.UserInterruption;
31 import hudson.model.queue.Executables;
32 import hudson.model.queue.SubTask;
33 import hudson.model.queue.Tasks;
34 import hudson.model.queue.WorkUnit;
35 import hudson.util.TimeUnit2;
36 import hudson.util.InterceptingProxy;
37 import hudson.security.ACL;
38 import jenkins.model.InterruptedBuildAction;
39 import jenkins.model.Jenkins;
40 import org.acegisecurity.Authentication;
41 import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
42 import org.kohsuke.stapler.HttpResponse;
43 import org.kohsuke.stapler.HttpResponses;
44 import org.kohsuke.stapler.StaplerRequest;
45 import org.kohsuke.stapler.StaplerResponse;
46 import org.kohsuke.stapler.export.ExportedBean;
47 import org.kohsuke.stapler.export.Exported;
48
49 import javax.servlet.ServletException;
50 import java.io.IOException;
51 import java.util.ArrayList;
52 import java.util.List;
53 import java.util.Vector;
54 import java.util.logging.Logger;
55 import java.util.logging.Level;
56 import java.lang.reflect.Method;
57
58 import static hudson.model.queue.Executables.*;
59 import static java.util.logging.Level.FINE;
60 import org.kohsuke.stapler.interceptor.RequirePOST;
61
62
63 /**
64 * Thread that executes builds.
65 *
66 * @author Kohsuke Kawaguchi
67 */
68 @ExportedBean
69 public class Executor extends Thread implements ModelObject {
70 protected final Computer owner;
71 private final Queue queue;
72
73 private long startTime;
74 /**
75 * Used to track when a job was last executed.
76 */
77 private long finishTime;
78
79 /**
80 * Executor number that identifies it among other executors for the same {@link Computer}.
81 */
82 private int number;
83 /**
84 * {@link Queue.Executable} being executed right now, or null if the executor is idle.
85 */
86 private volatile Queue.Executable executable;
87
88 private volatile WorkUnit workUnit;
89
90 private Throwable causeOfDeath;
91
92 private boolean induceDeath;
93
94 /**
95 * When the executor is interrupted, we allow the code that interrupted the thread to override the
96 * result code it prefers.
97 */
98 private Result interruptStatus;
99
100 /**
101 * Cause of interruption. Access needs to be synchronized.
102 */
103 private final List<CauseOfInterruption> causes = new Vector<CauseOfInterruption>();
104
105 public Executor(Computer owner, int n) {
106 super("Executor #"+n+" for "+owner.getDisplayName());
107 this.owner = owner;
108 this.queue = Jenkins.getInstance().getQueue();
109 this.number = n;
110 }
111
112 @Override
113 public void interrupt() {
114 interrupt(Result.ABORTED);
115 }
116
117 /**
118 * Interrupt the execution,
119 * but instead of marking the build as aborted, mark it as specified result.
120 *
121 * @since 1.417
122 */
123 public void interrupt(Result result) {
124 Authentication a = Jenkins.getAuthentication();
125 if(a instanceof AnonymousAuthenticationToken || a==ACL.SYSTEM)
126 interrupt(result, new CauseOfInterruption[0]);
127 else {
128 // worth recording who did it
129 // avoid using User.get() to avoid deadlock.
130 interrupt(result, new UserInterruption(a.getName()));
131 }
132 }
133
134 /**
135 * Interrupt the execution. Mark the cause and the status accordingly.
136 */
137 public void interrupt(Result result, CauseOfInterruption... causes) {
138 interruptStatus = result;
139 synchronized (this.causes) {
140 for (CauseOfInterruption c : causes) {
141 if (!this.causes.contains(c))
142 this.causes.add(c);
143 }
144 }
145 super.interrupt();
146 }
147
148 public Result abortResult() {
149 Result r = interruptStatus;
150 if (r==null) r = Result.ABORTED; // this is when we programmatically throw InterruptedException instead of calling the interrupt method.
151 return r;
152 }
153
154 /**
155 * report cause of interruption and record it to the build, if available.
156 *
157 * @since 1.425
158 */
159 public void recordCauseOfInterruption(Run<?,?> build, TaskListener listener) {
160 List<CauseOfInterruption> r;
161
162 // atomically get&clear causes, and minimize the lock.
163 synchronized (causes) {
164 if (causes.isEmpty()) return;
165 r = new ArrayList<CauseOfInterruption>(causes);
166 causes.clear();
167 }
168
169 build.addAction(new InterruptedBuildAction(r));
170 for (CauseOfInterruption c : r)
171 c.print(listener);
172 }
173
174 @Override
175 public void run() {
176 // run as the system user. see ACL.SYSTEM for more discussion about why this is somewhat broken
177 ACL.impersonate(ACL.SYSTEM);
178
179 try {
180 finishTime = System.currentTimeMillis();
181 while(shouldRun()) {
182 executable = null;
183 workUnit = null;
184 interruptStatus = null;
185 causes.clear();
186
187 synchronized(owner) {
188 if(owner.getNumExecutors()<owner.getExecutors().size()) {
189 // we've got too many executors.
190 owner.removeExecutor(this);
191 return;
192 }
193 }
194
195 // clear the interrupt flag as a precaution.
196 // sometime an interrupt aborts a build but without clearing the flag.
197 // see issue #1583
198 if (Thread.interrupted()) continue;
199 if (induceDeath) throw new ThreadDeath();
200
201 SubTask task;
202 try {
203 // transition from idle to building.
204 // perform this state change as an atomic operation wrt other queue operations
205 synchronized (queue) {
206 workUnit = grabJob();
207 workUnit.setExecutor(this);
208 if (LOGGER.isLoggable(FINE))
209 LOGGER.log(FINE, getName()+" grabbed "+workUnit+" from queue");
210 task = workUnit.work;
211 startTime = System.currentTimeMillis();
212 executable = task.createExecutable();
213 }
214 if (LOGGER.isLoggable(FINE))
215 LOGGER.log(FINE, getName()+" is going to execute "+executable);
216 } catch (IOException e) {
217 LOGGER.log(Level.SEVERE, "Executor threw an exception", e);
218 continue;
219 } catch (InterruptedException e) {
220 LOGGER.log(FINE, getName()+" interrupted",e);
221 continue;
222 }
223
224 Throwable problems = null;
225 final String threadName = getName();
226 try {
227 workUnit.context.synchronizeStart();
228
229 if (executable instanceof Actionable) {
230 for (Action action: workUnit.context.actions) {
231 ((Actionable) executable).addAction(action);
232 }
233 }
234 setName(threadName+" : executing "+executable.toString());
235 if (LOGGER.isLoggable(FINE))
236 LOGGER.log(FINE, getName()+" is now executing "+executable);
237 queue.execute(executable, task);
238 } catch (Throwable e) {
239 // for some reason the executor died. this is really
240 // a bug in the code, but we don't want the executor to die,
241 // so just leave some info and go on to build other things
242 LOGGER.log(Level.SEVERE, "Executor threw an exception", e);
243 workUnit.context.abort(e);
244 problems = e;
245 } finally {
246 setName(threadName);
247 finishTime = System.currentTimeMillis();
248 if (LOGGER.isLoggable(FINE))
249 LOGGER.log(FINE, getName()+" completed "+executable+" in "+(finishTime-startTime)+"ms");
250 try {
251 workUnit.context.synchronizeEnd(executable,problems,finishTime - startTime);
252 } catch (InterruptedException e) {
253 workUnit.context.abort(e);
254 continue;
255 } finally {
256 workUnit.setExecutor(null);
257 }
258 }
259 }
260 } catch(RuntimeException e) {
261 causeOfDeath = e;
262 throw e;
263 } catch (Error e) {
264 causeOfDeath = e;
265 throw e;
266 }
267 }
268
269 /**
270 * For testing only. Simulate a fatal unexpected failure.
271 */
272 public void killHard() {
273 induceDeath = true;
274 interrupt();
275 }
276
277 /**
278 * Returns true if we should keep going.
279 */
280 protected boolean shouldRun() {
281 return Jenkins.getInstance() != null && !Jenkins.getInstance().isTerminating();
282 }
283
284 protected WorkUnit grabJob() throws InterruptedException {
285 return queue.pop();
286 }
287
288 /**
289 * Returns the current {@link Queue.Task} this executor is running.
290 *
291 * @return
292 * null if the executor is idle.
293 */
294 @Exported
295 public Queue.Executable getCurrentExecutable() {
296 return executable;
297 }
298
299 /**
300 * Returns the current {@link WorkUnit} (of {@link #getCurrentExecutable() the current executable})
301 * that this executor is running.
302 *
303 * @return
304 * null if the executor is idle.
305 */
306 @Exported
307 public WorkUnit getCurrentWorkUnit() {
308 return workUnit;
309 }
310
311 /**
312 * If {@linkplain #getCurrentExecutable() current executable} is {@link AbstractBuild},
313 * return the workspace that this executor is using, or null if the build hasn't gotten
314 * to that point yet.
315 */
316 public FilePath getCurrentWorkspace() {
317 Executable e = executable;
318 if(e==null) return null;
319 if (e instanceof AbstractBuild) {
320 AbstractBuild ab = (AbstractBuild) e;
321 return ab.getWorkspace();
322 }
323 return null;
324 }
325
326 /**
327 * Same as {@link #getName()}.
328 */
329 public String getDisplayName() {
330 return "Executor #"+getNumber();
331 }
332
333 /**
334 * Gets the executor number that uniquely identifies it among
335 * other {@link Executor}s for the same computer.
336 *
337 * @return
338 * a sequential number starting from 0.
339 */
340 @Exported
341 public int getNumber() {
342 return number;
343 }
344
345 /**
346 * Returns true if this {@link Executor} is ready for action.
347 */
348 @Exported
349 public boolean isIdle() {
350 return executable==null;
351 }
352
353 /**
354 * The opposite of {@link #isIdle()} &mdash; the executor is doing some work.
355 */
356 public boolean isBusy() {
357 return executable!=null;
358 }
359
360 /**
361 * If this thread dies unexpectedly, obtain the cause of the failure.
362 *
363 * @return null if the death is expected death or the thread is {@link #isAlive() still alive}.
364 * @since 1.142
365 */
366 public Throwable getCauseOfDeath() {
367 return causeOfDeath;
368 }
369
370 /**
371 * Returns the progress of the current build in the number between 0-100.
372 *
373 * @return -1
374 * if it's impossible to estimate the progress.
375 */
376 @Exported
377 public int getProgress() {
378 Queue.Executable e = executable;
379 if(e==null) return -1;
380 long d = Executables.getEstimatedDurationFor(e);
381 if(d<0) return -1;
382
383 int num = (int)(getElapsedTime()*100/d);
384 if(num>=100) num=99;
385 return num;
386 }
387
388 /**
389 * Returns true if the current build is likely stuck.
390 *
391 * <p>
392 * This is a heuristics based approach, but if the build is suspiciously taking for a long time,
393 * this method returns true.
394 */
395 @Exported
396 public boolean isLikelyStuck() {
397 Queue.Executable e = executable;
398 if(e==null) return false;
399
400 long elapsed = getElapsedTime();
401 long d = Executables.getEstimatedDurationFor(e);
402 if(d>=0) {
403 // if it's taking 10 times longer than ETA, consider it stuck
404 return d*10 < elapsed;
405 } else {
406 // if no ETA is available, a build taking longer than a day is considered stuck
407 return TimeUnit2.MILLISECONDS.toHours(elapsed)>24;
408 }
409 }
410
411 public long getElapsedTime() {
412 return System.currentTimeMillis() - startTime;
413 }
414
415 /**
416 * Returns the number of milli-seconds the currently executing job spent in the queue
417 * waiting for an available executor. This excludes the quiet period time of the job.
418 * @since 1.440
419 */
420 public long getTimeSpentInQueue() {
421 return startTime - workUnit.context.item.buildableStartMilliseconds;
422 }
423
424 /**
425 * Gets the string that says how long since this build has started.
426 *
427 * @return
428 * string like "3 minutes" "1 day" etc.
429 */
430 public String getTimestampString() {
431 return Util.getPastTimeString(getElapsedTime());
432 }
433
434 /**
435 * Computes a human-readable text that shows the expected remaining time
436 * until the build completes.
437 */
438 public String getEstimatedRemainingTime() {
439 Queue.Executable e = executable;
440 if(e==null) return Messages.Executor_NotAvailable();
441
442 long d = Executables.getEstimatedDurationFor(e);
443 if(d<0) return Messages.Executor_NotAvailable();
444
445 long eta = d-getElapsedTime();
446 if(eta<=0) return Messages.Executor_NotAvailable();
447
448 return Util.getTimeSpanString(eta);
449 }
450
451 /**
452 * The same as {@link #getEstimatedRemainingTime()} but return
453 * it as a number of milli-seconds.
454 */
455 public long getEstimatedRemainingTimeMillis() {
456 Queue.Executable e = executable;
457 if(e==null) return -1;
458
459 long d = Executables.getEstimatedDurationFor(e);
460 if(d<0) return -1;
461
462 long eta = d-getElapsedTime();
463 if(eta<=0) return -1;
464
465 return eta;
466 }
467
468 /**
469 * @deprecated as of 1.489
470 * Use {@link #doStop()}.
471 */
472 @RequirePOST
473 public void doStop( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
474 doStop().generateResponse(req,rsp,this);
475 }
476
477 /**
478 * Stops the current build.
479 *
480 * @since 1.489
481 */
482 public HttpResponse doStop() {
483 Queue.Executable e = executable;
484 if(e!=null) {
485 Tasks.getOwnerTaskOf(getParentOf(e)).checkAbortPermission();
486 interrupt();
487 }
488 return HttpResponses.forwardToPreviousPage();
489 }
490
491 /**
492 * Throws away this executor and get a new one.
493 */
494 public HttpResponse doYank() {
495 Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
496 if (isAlive())
497 throw new Failure("Can't yank a live executor");
498 owner.removeExecutor(this);
499 return HttpResponses.redirectViaContextPath("/");
500 }
501
502 /**
503 * Checks if the current user has a permission to stop this build.
504 */
505 public boolean hasStopPermission() {
506 Queue.Executable e = executable;
507 return e!=null && Tasks.getOwnerTaskOf(getParentOf(e)).hasAbortPermission();
508 }
509
510 public Computer getOwner() {
511 return owner;
512 }
513
514 /**
515 * Returns when this executor started or should start being idle.
516 */
517 public long getIdleStartMilliseconds() {
518 if (isIdle())
519 return Math.max(finishTime, owner.getConnectTime());
520 else {
521 return Math.max(startTime + Math.max(0, Executables.getEstimatedDurationFor(executable)),
522 System.currentTimeMillis() + 15000);
523 }
524 }
525
526 /**
527 * Exposes the executor to the remote API.
528 */
529 public Api getApi() {
530 return new Api(this);
531 }
532
533 /**
534 * Creates a proxy object that executes the callee in the context that impersonates
535 * this executor. Useful to export an object to a remote channel.
536 */
537 public <T> T newImpersonatingProxy(Class<T> type, T core) {
538 return new InterceptingProxy() {
539 protected Object call(Object o, Method m, Object[] args) throws Throwable {
540 final Executor old = IMPERSONATION.get();
541 IMPERSONATION.set(Executor.this);
542 try {
543 return m.invoke(o,args);
544 } finally {
545 IMPERSONATION.set(old);
546 }
547 }
548 }.wrap(type,core);
549 }
550
551 /**
552 * Returns the executor of the current thread or null if current thread is not an executor.
553 */
554 public static Executor currentExecutor() {
555 Thread t = Thread.currentThread();
556 if (t instanceof Executor) return (Executor) t;
557 return IMPERSONATION.get();
558 }
559
560 /**
561 * Returns the estimated duration for the executable.
562 * Protects against {@link AbstractMethodError}s if the {@link Executable} implementation
563 * was compiled against Hudson < 1.383
564 *
565 * @deprecated as of 1.388
566 * Use {@link Executables#getEstimatedDurationFor(Executable)}
567 */
568 public static long getEstimatedDurationFor(Executable e) {
569 return Executables.getEstimatedDurationFor(e);
570 }
571
572 /**
573 * Mechanism to allow threads (in particular the channel request handling threads) to
574 * run on behalf of {@link Executor}.
575 */
576 private static final ThreadLocal<Executor> IMPERSONATION = new ThreadLocal<Executor>();
577
578 private static final Logger LOGGER = Logger.getLogger(Executor.class.getName());
579
580 }
581