1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| public @CheckForNull V start() throws Exception { V result = null; int currentAttempt = 0; boolean success = false;
while (currentAttempt < attempts && !success) { currentAttempt++; try { if (LOGGER.isLoggable(Level.INFO)) { LOGGER.log(Level.INFO, Messages.Retrier_Attempt(currentAttempt, action)); } result = callable.call();
} catch (Exception e) { if (duringActionExceptions == null || Stream.of(duringActionExceptions).noneMatch(exception -> exception.isAssignableFrom(e.getClass()))) { // if the raised exception is not considered as a controlled exception doing the action, rethrow it LOGGER.log(Level.WARNING, Messages.Retrier_ExceptionThrown(currentAttempt, action), e); throw e; } else { // if the exception is considered as a failed action, notify it to the listener LOGGER.log(Level.INFO, Messages.Retrier_ExceptionFailed(currentAttempt, action), e); if (duringActionExceptionListener != null) { LOGGER.log(Level.INFO, Messages.Retrier_CallingListener(e.getLocalizedMessage(), currentAttempt, action)); result = duringActionExceptionListener.apply(currentAttempt, e); } } }
// After the call and the call to the listener, which can change the result, test the result success = checkResult.test(currentAttempt, result); if (!success) { if (currentAttempt < attempts) { LOGGER.log(Level.WARNING, Messages.Retrier_AttemptFailed(currentAttempt, action)); LOGGER.log(Level.FINE, Messages.Retrier_Sleeping(delay, action)); try { Thread.sleep(delay); } catch (InterruptedException ie) { LOGGER.log(Level.FINE, Messages.Retrier_Interruption(action)); Thread.currentThread().interrupt(); // flag this thread as interrupted currentAttempt = attempts; // finish } } else { // Failed to perform the action LOGGER.log(Level.INFO, Messages.Retrier_NoSuccess(action, attempts)); } } else { LOGGER.log(Level.INFO, Messages.Retrier_Success(action, currentAttempt)); } }
return result; }
result = duringActionExceptionListener.apply(currentAttempt, e); 我们看这个 result怎么生成的, 是调用了duringActionExceptionListener变量的apply() 方法,
这个 duringActionExceptionListener 是什么玩意呢? 是个 private BiFunction<Integer, Exception, V> duringActionExceptionListener; 这个变量是 在 这里 .withDuringActionExceptionListener((attempt, e) -> FormValidation.errorWithMarkup(e.getClass().getSimpleName() + ": " + e.getLocalizedMessage())) 给传入进去的。
我们发现这个 玩意是一个 java lambda 表达式,其实也就是一个匿名方法,具体就是 类似下面这样,方法接收2个参数。在上面 调用 apply 时候传入这2个参数。 f(attempt, e) { return FormValidation.errorWithMarkup( e.getClass().getSimpleName() + ": " + e.getLocalizedMessage() ) }
其实 调用 apply(currentAttempt, e) 就是调用这个 lambda表达式, 也就是调用 FormValidation.errorWithMarkup() 这个方法了,可以看到 e 就是 先前抛出的 异常, 好像 currentAttempt 这个参数没有用到。 errorWithMarkup() 方法会 返回个 FormValidation ,有字符串错误 生成个 FormValidation 对象。 FormValidation 继承了 IOException 实现了 HttpResponse 接口的。这个里面有个 public abstract String renderHtml(); 需要重新实现的。 这个就是 拼接出来html展示的字符串了。里面是用 div 来拼接出来的。 "<div class=" + kind.name().toLowerCase(Locale.ENGLISH) + "> <img src='" + req.getContextPath() + Jenkins.RESOURCE_PATH + "/images/none.gif' height=16 width=1>" + message + "</div>"; 到这里 我最后返回的 result 值已经生成初始化好了。result 是 FormValidation 对象。
接着 Retrier.java 类中的 start() 方法中 又做了一 个 判断
做的检查判断 是 success = checkResult.test(currentAttempt, result); 这个 checkResult 来自 初始化 Retrier.Builder<> 的第三个参数, 也是一个 lambda 表达式 (currentAttempt, result) -> result.kind == FormValidation.Kind.OK, 类似就是下面这个,方法接收2个参数。在上面 调用 test 时候传入这2个参数。 f(currentAttempt, result) { return result.kind == FormValidation.Kind.OK }
最后 就 返回 这个 result 给开头的那个 doCheckUpdatesServer() 方法了。
最后 通过 lastErrorCheckUpdateCenters = Messages.PluginManager_CheckUpdateServerError(result.getMessage()); 吧这个result 格式化配上本地化字符串赋值给lastErrorCheckUpdateCenters变量了。
在 src/main/resources/hudson/PluginManager/advanced.jelly 中的 check.jelly 文件中会 显示 这个 lastErrorCheckUpdateCenters 变量的值到 web 页面上。
|