You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CeProcessingSchedulerImplTest.java 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2019 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.ce.taskprocessor;
  21. import com.google.common.util.concurrent.ListenableFuture;
  22. import com.google.common.util.concurrent.ListenableScheduledFuture;
  23. import com.google.common.util.concurrent.ListeningScheduledExecutorService;
  24. import com.google.common.util.concurrent.MoreExecutors;
  25. import java.util.ArrayList;
  26. import java.util.Collection;
  27. import java.util.Iterator;
  28. import java.util.List;
  29. import java.util.Objects;
  30. import java.util.Queue;
  31. import java.util.Random;
  32. import java.util.Set;
  33. import java.util.concurrent.Callable;
  34. import java.util.concurrent.ConcurrentLinkedQueue;
  35. import java.util.concurrent.Delayed;
  36. import java.util.concurrent.ExecutionException;
  37. import java.util.concurrent.Future;
  38. import java.util.concurrent.ScheduledExecutorService;
  39. import java.util.concurrent.ScheduledFuture;
  40. import java.util.concurrent.TimeUnit;
  41. import java.util.concurrent.TimeoutException;
  42. import javax.annotation.Nullable;
  43. import javax.annotation.concurrent.Immutable;
  44. import org.junit.Rule;
  45. import org.junit.Test;
  46. import org.junit.rules.DisableOnDebug;
  47. import org.junit.rules.TestRule;
  48. import org.junit.rules.Timeout;
  49. import org.sonar.ce.configuration.CeConfigurationRule;
  50. import static com.google.common.collect.ImmutableList.copyOf;
  51. import static java.util.Collections.emptySet;
  52. import static java.util.concurrent.TimeUnit.MILLISECONDS;
  53. import static org.assertj.core.api.Assertions.assertThat;
  54. import static org.mockito.ArgumentMatchers.any;
  55. import static org.mockito.ArgumentMatchers.eq;
  56. import static org.mockito.Mockito.mock;
  57. import static org.mockito.Mockito.spy;
  58. import static org.mockito.Mockito.times;
  59. import static org.mockito.Mockito.verify;
  60. import static org.mockito.Mockito.when;
  61. import static org.sonar.ce.taskprocessor.CeWorker.Result.DISABLED;
  62. import static org.sonar.ce.taskprocessor.CeWorker.Result.NO_TASK;
  63. import static org.sonar.ce.taskprocessor.CeWorker.Result.TASK_PROCESSED;
  64. public class CeProcessingSchedulerImplTest {
  65. private static final Error ERROR_TO_INTERRUPT_CHAINING = new Error("Error should stop scheduling");
  66. // due to risks of infinite chaining of tasks/futures, a timeout is required for safety
  67. @Rule
  68. public TestRule safeguardTimeout = new DisableOnDebug(Timeout.seconds(60));
  69. @Rule
  70. public CeConfigurationRule ceConfiguration = new CeConfigurationRule();
  71. private CeWorker ceWorker = mock(CeWorker.class);
  72. private CeWorkerFactory ceWorkerFactory = new TestCeWorkerFactory(ceWorker);
  73. private StubCeProcessingSchedulerExecutorService processingExecutorService = new StubCeProcessingSchedulerExecutorService();
  74. private SchedulerCall regularDelayedPoll = new SchedulerCall(ceWorker, 2000L, MILLISECONDS);
  75. private SchedulerCall extendedDelayedPoll = new SchedulerCall(ceWorker, 30000L, MILLISECONDS);
  76. private SchedulerCall notDelayedPoll = new SchedulerCall(ceWorker);
  77. private CeWorkerController ceWorkerController = new CeWorkerControllerImpl(ceConfiguration);
  78. private CeProcessingSchedulerImpl underTest = new CeProcessingSchedulerImpl(ceConfiguration, processingExecutorService, ceWorkerFactory, ceWorkerController);
  79. @Test
  80. public void polls_without_delay_when_CeWorkerCallable_returns_TASK_PROCESSED() throws Exception {
  81. when(ceWorker.call())
  82. .thenReturn(TASK_PROCESSED)
  83. .thenThrow(ERROR_TO_INTERRUPT_CHAINING);
  84. startSchedulingAndRun();
  85. assertThat(processingExecutorService.getSchedulerCalls()).containsOnly(
  86. regularDelayedPoll,
  87. notDelayedPoll);
  88. }
  89. @Test
  90. public void polls_without_delay_when_CeWorkerCallable_throws_Exception_but_not_Error() throws Exception {
  91. when(ceWorker.call())
  92. .thenThrow(new Exception("Exception is followed by a poll without delay"))
  93. .thenThrow(ERROR_TO_INTERRUPT_CHAINING);
  94. startSchedulingAndRun();
  95. assertThat(processingExecutorService.getSchedulerCalls()).containsExactly(
  96. regularDelayedPoll,
  97. notDelayedPoll);
  98. }
  99. @Test
  100. public void polls_with_regular_delay_when_CeWorkerCallable_returns_NO_TASK() throws Exception {
  101. when(ceWorker.call())
  102. .thenReturn(NO_TASK)
  103. .thenThrow(ERROR_TO_INTERRUPT_CHAINING);
  104. startSchedulingAndRun();
  105. assertThat(processingExecutorService.getSchedulerCalls()).containsExactly(
  106. regularDelayedPoll,
  107. regularDelayedPoll);
  108. }
  109. @Test
  110. public void polls_with_extended_delay_when_CeWorkerCallable_returns_DISABLED() throws Exception {
  111. when(ceWorker.call())
  112. .thenReturn(DISABLED)
  113. .thenThrow(ERROR_TO_INTERRUPT_CHAINING);
  114. startSchedulingAndRun();
  115. assertThat(processingExecutorService.getSchedulerCalls()).containsExactly(
  116. regularDelayedPoll,
  117. extendedDelayedPoll);
  118. }
  119. @Test
  120. public void startScheduling_schedules_CeWorkerCallable_at_fixed_rate_run_head_of_queue() throws Exception {
  121. when(ceWorker.call())
  122. .thenReturn(TASK_PROCESSED)
  123. .thenReturn(TASK_PROCESSED)
  124. .thenReturn(NO_TASK)
  125. .thenReturn(TASK_PROCESSED)
  126. .thenReturn(NO_TASK)
  127. .thenThrow(new Exception("IAE should not cause scheduling to stop"))
  128. .thenReturn(NO_TASK)
  129. .thenReturn(NO_TASK)
  130. .thenReturn(NO_TASK)
  131. .thenThrow(ERROR_TO_INTERRUPT_CHAINING);
  132. startSchedulingAndRun();
  133. assertThat(processingExecutorService.getSchedulerCalls()).containsExactly(
  134. regularDelayedPoll,
  135. notDelayedPoll,
  136. notDelayedPoll,
  137. regularDelayedPoll,
  138. notDelayedPoll,
  139. regularDelayedPoll,
  140. notDelayedPoll,
  141. regularDelayedPoll,
  142. regularDelayedPoll,
  143. regularDelayedPoll);
  144. }
  145. @Test
  146. public void gracefulStopScheduling_cancels_next_polling_and_does_not_add_any_new_one() throws Exception {
  147. when(ceWorker.call())
  148. .thenReturn(NO_TASK)
  149. .thenReturn(TASK_PROCESSED)
  150. .thenReturn(NO_TASK)
  151. .thenReturn(NO_TASK)
  152. .thenReturn(NO_TASK)
  153. .thenReturn(NO_TASK)
  154. .thenReturn(NO_TASK)
  155. .thenThrow(ERROR_TO_INTERRUPT_CHAINING);
  156. underTest.startScheduling();
  157. int cancelledTaskFutureCount = 0;
  158. int i = 0;
  159. while (processingExecutorService.futures.peek() != null) {
  160. Future<?> future = processingExecutorService.futures.poll();
  161. if (future.isCancelled()) {
  162. cancelledTaskFutureCount++;
  163. } else {
  164. future.get();
  165. }
  166. // call for graceful after second delayed polling
  167. if (i == 1) {
  168. underTest.gracefulStopScheduling();
  169. }
  170. i++;
  171. }
  172. assertThat(cancelledTaskFutureCount).isEqualTo(1);
  173. assertThat(processingExecutorService.getSchedulerCalls()).containsExactly(
  174. regularDelayedPoll,
  175. regularDelayedPoll,
  176. notDelayedPoll,
  177. regularDelayedPoll);
  178. }
  179. @Test
  180. public void stopScheduling_cancels_next_polling_and_does_not_add_any_new_one() throws Exception {
  181. when(ceWorker.call())
  182. .thenReturn(NO_TASK)
  183. .thenReturn(TASK_PROCESSED)
  184. .thenReturn(NO_TASK)
  185. .thenReturn(NO_TASK)
  186. .thenReturn(NO_TASK)
  187. .thenReturn(NO_TASK)
  188. .thenReturn(NO_TASK)
  189. .thenThrow(ERROR_TO_INTERRUPT_CHAINING);
  190. underTest.startScheduling();
  191. int cancelledTaskFutureCount = 0;
  192. int i = 0;
  193. while (processingExecutorService.futures.peek() != null) {
  194. Future<?> future = processingExecutorService.futures.poll();
  195. if (future.isCancelled()) {
  196. cancelledTaskFutureCount++;
  197. } else {
  198. future.get();
  199. }
  200. // call stop after second delayed polling
  201. if (i == 1) {
  202. underTest.hardStopScheduling();
  203. }
  204. i++;
  205. }
  206. assertThat(cancelledTaskFutureCount).isEqualTo(1);
  207. assertThat(processingExecutorService.getSchedulerCalls()).containsExactly(
  208. regularDelayedPoll,
  209. regularDelayedPoll,
  210. notDelayedPoll,
  211. regularDelayedPoll);
  212. }
  213. @Test
  214. public void when_workerCount_is_more_than_1_as_many_CeWorkerCallable_are_scheduled() throws Exception {
  215. int workerCount = Math.abs(new Random().nextInt(10)) + 1;
  216. ceConfiguration.setWorkerThreadCount(workerCount);
  217. CeWorker[] workers = new CeWorker[workerCount];
  218. for (int i = 0; i < workerCount; i++) {
  219. workers[i] = mock(CeWorker.class);
  220. when(workers[i].call())
  221. .thenReturn(NO_TASK)
  222. .thenThrow(ERROR_TO_INTERRUPT_CHAINING);
  223. }
  224. ListenableScheduledFuture listenableScheduledFuture = mock(ListenableScheduledFuture.class);
  225. CeProcessingSchedulerExecutorService processingExecutorService = mock(CeProcessingSchedulerExecutorService.class);
  226. when(processingExecutorService.schedule(any(CeWorker.class), any(Long.class), any(TimeUnit.class))).thenReturn(listenableScheduledFuture);
  227. CeWorkerFactory ceWorkerFactory = spy(new TestCeWorkerFactory(workers));
  228. CeProcessingSchedulerImpl underTest = new CeProcessingSchedulerImpl(ceConfiguration, processingExecutorService, ceWorkerFactory, ceWorkerController);
  229. when(processingExecutorService.schedule(ceWorker, ceConfiguration.getQueuePollingDelay(), MILLISECONDS))
  230. .thenReturn(listenableScheduledFuture);
  231. underTest.startScheduling();
  232. // No exception from TestCeWorkerFactory must be thrown
  233. // Verify that schedule has been called on all workers
  234. for (int i = 0; i < workerCount; i++) {
  235. verify(processingExecutorService).schedule(workers[i], ceConfiguration.getQueuePollingDelay(), MILLISECONDS);
  236. }
  237. verify(listenableScheduledFuture, times(workerCount)).addListener(any(Runnable.class), eq(MoreExecutors.directExecutor()));
  238. for (int i = 0; i < workerCount; i++) {
  239. verify(ceWorkerFactory).create(i);
  240. }
  241. }
  242. private void startSchedulingAndRun() throws ExecutionException, InterruptedException {
  243. underTest.startScheduling();
  244. // execute future synchronously
  245. processingExecutorService.runFutures();
  246. }
  247. private static class TestCeWorkerFactory implements CeWorkerFactory {
  248. private final Iterator<CeWorker> ceWorkers;
  249. private TestCeWorkerFactory(CeWorker... ceWorkers) {
  250. this.ceWorkers = copyOf(ceWorkers).iterator();
  251. }
  252. @Override
  253. public CeWorker create(int ordinal) {
  254. // This will throw an NoSuchElementException if there are too many calls
  255. return ceWorkers.next();
  256. }
  257. @Override
  258. public Set<CeWorker> getWorkers() {
  259. return emptySet();
  260. }
  261. }
  262. /**
  263. * A synchronous implementation of {@link CeProcessingSchedulerExecutorService} which exposes a synchronous
  264. * method to execute futures it creates and exposes a method to retrieve logs of calls to
  265. * {@link CeProcessingSchedulerExecutorService#schedule(Callable, long, TimeUnit)} which is used by
  266. * {@link CeProcessingSchedulerImpl}.
  267. */
  268. private static class StubCeProcessingSchedulerExecutorService implements CeProcessingSchedulerExecutorService {
  269. private final Queue<Future<?>> futures = new ConcurrentLinkedQueue<>();
  270. private final ListeningScheduledExecutorService delegate = MoreExecutors.listeningDecorator(new SynchronousStubExecutorService());
  271. private final List<SchedulerCall> schedulerCalls = new ArrayList<>();
  272. public List<SchedulerCall> getSchedulerCalls() {
  273. return schedulerCalls;
  274. }
  275. public void runFutures() throws ExecutionException, InterruptedException {
  276. while (futures.peek() != null) {
  277. Future<?> future = futures.poll();
  278. if (!future.isCancelled()) {
  279. future.get();
  280. }
  281. }
  282. }
  283. @Override
  284. public <V> ListenableScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
  285. this.schedulerCalls.add(new SchedulerCall(callable, delay, unit));
  286. return delegate.schedule(callable, delay, unit);
  287. }
  288. @Override
  289. public <T> ListenableFuture<T> submit(Callable<T> task) {
  290. this.schedulerCalls.add(new SchedulerCall(task));
  291. return delegate.submit(task);
  292. }
  293. @Override
  294. public void stop() {
  295. throw new UnsupportedOperationException("stop() not implemented");
  296. }
  297. // ////////////// delegated methods ////////////////
  298. @Override
  299. public ListenableScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
  300. return delegate.schedule(command, delay, unit);
  301. }
  302. @Override
  303. public ListenableScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
  304. return delegate.scheduleAtFixedRate(command, initialDelay, period, unit);
  305. }
  306. @Override
  307. public ListenableScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
  308. return delegate.scheduleWithFixedDelay(command, initialDelay, delay, unit);
  309. }
  310. @Override
  311. public void shutdown() {
  312. delegate.shutdown();
  313. }
  314. @Override
  315. public List<Runnable> shutdownNow() {
  316. return delegate.shutdownNow();
  317. }
  318. @Override
  319. public boolean isShutdown() {
  320. return delegate.isShutdown();
  321. }
  322. @Override
  323. public boolean isTerminated() {
  324. return delegate.isTerminated();
  325. }
  326. @Override
  327. public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
  328. return delegate.awaitTermination(timeout, unit);
  329. }
  330. @Override
  331. public <T> ListenableFuture<T> submit(Runnable task, T result) {
  332. return delegate.submit(task, result);
  333. }
  334. @Override
  335. public ListenableFuture<?> submit(Runnable task) {
  336. return delegate.submit(task);
  337. }
  338. @Override
  339. public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
  340. return delegate.invokeAll(tasks);
  341. }
  342. @Override
  343. public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
  344. return delegate.invokeAll(tasks, timeout, unit);
  345. }
  346. @Override
  347. public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
  348. return delegate.invokeAny(tasks);
  349. }
  350. @Override
  351. public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
  352. return delegate.invokeAny(tasks, timeout, unit);
  353. }
  354. @Override
  355. public void execute(Runnable command) {
  356. delegate.execute(command);
  357. }
  358. /**
  359. * A partial (only 3 methods) implementation of ScheduledExecutorService which stores futures it creates into
  360. * {@link StubCeProcessingSchedulerExecutorService#futures}.
  361. */
  362. private class SynchronousStubExecutorService implements ScheduledExecutorService {
  363. @Override
  364. public ScheduledFuture<?> schedule(final Runnable command, long delay, TimeUnit unit) {
  365. ScheduledFuture<Void> res = new AbstractPartiallyImplementedScheduledFuture<Void>() {
  366. @Override
  367. public Void get() {
  368. command.run();
  369. return null;
  370. }
  371. };
  372. futures.add(res);
  373. return res;
  374. }
  375. @Override
  376. public <V> ScheduledFuture<V> schedule(final Callable<V> callable, long delay, TimeUnit unit) {
  377. ScheduledFuture<V> res = new AbstractPartiallyImplementedScheduledFuture<V>() {
  378. @Override
  379. public V get() throws ExecutionException {
  380. try {
  381. return callable.call();
  382. } catch (Exception e) {
  383. throw new ExecutionException(e);
  384. }
  385. }
  386. };
  387. futures.add(res);
  388. return res;
  389. }
  390. @Override
  391. public <T> Future<T> submit(final Callable<T> task) {
  392. Future<T> res = new AbstractPartiallyImplementedFuture<T>() {
  393. @Override
  394. public T get() throws ExecutionException {
  395. try {
  396. return task.call();
  397. } catch (Exception e) {
  398. throw new ExecutionException(e);
  399. }
  400. }
  401. };
  402. futures.add(res);
  403. return res;
  404. }
  405. @Override
  406. public void execute(Runnable command) {
  407. command.run();
  408. }
  409. /////////// unsupported operations ///////////
  410. @Override
  411. public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
  412. throw new UnsupportedOperationException("scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) not implemented");
  413. }
  414. @Override
  415. public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
  416. throw new UnsupportedOperationException("scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) not implemented");
  417. }
  418. @Override
  419. public void shutdown() {
  420. // Nothing to do
  421. }
  422. @Override
  423. public List<Runnable> shutdownNow() {
  424. throw new UnsupportedOperationException("shutdownNow() not implemented");
  425. }
  426. @Override
  427. public boolean isShutdown() {
  428. throw new UnsupportedOperationException("isShutdown() not implemented");
  429. }
  430. @Override
  431. public boolean isTerminated() {
  432. throw new UnsupportedOperationException("isTerminated() not implemented");
  433. }
  434. @Override
  435. public boolean awaitTermination(long timeout, TimeUnit unit) {
  436. throw new UnsupportedOperationException("awaitTermination(long timeout, TimeUnit unit) not implemented");
  437. }
  438. @Override
  439. public <T> Future<T> submit(Runnable task, T result) {
  440. throw new UnsupportedOperationException("submit(Runnable task, T result) not implemented");
  441. }
  442. @Override
  443. public Future<?> submit(Runnable task) {
  444. throw new UnsupportedOperationException("submit(Runnable task) not implemented");
  445. }
  446. @Override
  447. public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) {
  448. throw new UnsupportedOperationException("invokeAll(Collection<? extends Callable<T>> tasks) not implemented");
  449. }
  450. @Override
  451. public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) {
  452. throw new UnsupportedOperationException("invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) not implemented");
  453. }
  454. @Override
  455. public <T> T invokeAny(Collection<? extends Callable<T>> tasks) {
  456. throw new UnsupportedOperationException("invokeAny(Collection<? extends Callable<T>> tasks) not implemented");
  457. }
  458. @Override
  459. public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) {
  460. throw new UnsupportedOperationException("invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) not implemented");
  461. }
  462. }
  463. }
  464. private static abstract class AbstractPartiallyImplementedScheduledFuture<V> extends AbstractPartiallyImplementedFuture<V> implements ScheduledFuture<V> {
  465. @Override
  466. public long getDelay(TimeUnit unit) {
  467. throw new UnsupportedOperationException("getDelay(TimeUnit unit) not implemented");
  468. }
  469. @Override
  470. public int compareTo(Delayed o) {
  471. throw new UnsupportedOperationException("compareTo(Delayed o) not implemented");
  472. }
  473. }
  474. private static abstract class AbstractPartiallyImplementedFuture<T> implements Future<T> {
  475. private boolean cancelled = false;
  476. @Override
  477. public boolean cancel(boolean mayInterruptIfRunning) {
  478. this.cancelled = true;
  479. return true;
  480. }
  481. @Override
  482. public boolean isCancelled() {
  483. return this.cancelled;
  484. }
  485. @Override
  486. public boolean isDone() {
  487. throw new UnsupportedOperationException("isDone() not implemented");
  488. }
  489. @Override
  490. public T get(long timeout, TimeUnit unit) {
  491. throw new UnsupportedOperationException("get(long timeout, TimeUnit unit) not implemented");
  492. }
  493. }
  494. /**
  495. * Used to log parameters of calls to {@link CeProcessingSchedulerExecutorService#schedule(Callable, long, TimeUnit)}
  496. */
  497. @Immutable
  498. private static final class SchedulerCall {
  499. private final Callable<?> callable;
  500. private final long delay;
  501. private final TimeUnit unit;
  502. private SchedulerCall(Callable<?> callable, long delay, TimeUnit unit) {
  503. this.callable = callable;
  504. this.delay = delay;
  505. this.unit = unit;
  506. }
  507. private SchedulerCall(Callable<?> callable) {
  508. this.callable = callable;
  509. this.delay = -63366;
  510. this.unit = TimeUnit.NANOSECONDS;
  511. }
  512. @Override
  513. public boolean equals(@Nullable Object o) {
  514. if (this == o) {
  515. return true;
  516. }
  517. if (o == null || getClass() != o.getClass()) {
  518. return false;
  519. }
  520. SchedulerCall that = (SchedulerCall) o;
  521. return delay == that.delay && callable == that.callable && unit.equals(that.unit);
  522. }
  523. @Override
  524. public int hashCode() {
  525. return Objects.hash(callable, delay, unit);
  526. }
  527. @Override
  528. public String toString() {
  529. return "SchedulerCall{" +
  530. "callable=" + callable +
  531. ", delay=" + delay +
  532. ", unit=" + unit +
  533. '}';
  534. }
  535. }
  536. }