/* * * * Copyright 2000-2014 JetBrains s.r.o. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * http://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package jetbrains.buildServer.clouds.base.connector; import com.intellij.openapi.diagnostic.Logger; import java.util.HashMap; import java.util.Map; import java.util.concurrent.*; import jetbrains.buildServer.util.NamedThreadFactory; import jetbrains.buildServer.util.executors.ExecutorsFactory; import org.jetbrains.annotations.NotNull; /** * @author Sergey.Pak * Date: 7/29/2014 * Time: 3:51 PM */ public class CloudAsyncTaskExecutor { private static final Logger LOG = Logger.getInstance(CloudAsyncTaskExecutor.class.getName()); private static final long LONG_TASK_TIME = 60*1000l; private final ScheduledExecutorService myExecutor; private final ConcurrentMap<AsyncCloudTask, TaskCallbackHandler> myExecutingTasks; private final Map<AsyncCloudTask, Long> myLongTasks = new HashMap<AsyncCloudTask, Long>(); public CloudAsyncTaskExecutor(String prefix) { myExecutingTasks = new ConcurrentHashMap<AsyncCloudTask, TaskCallbackHandler>(); myExecutor = ExecutorsFactory.newFixedScheduledDaemonExecutor(prefix, 2); scheduleWithFixedDelay("Check for tasks", new Runnable() { public void run() { checkTasks(); } }, 0, 300, TimeUnit.MILLISECONDS); } public void executeAsync(final AsyncCloudTask operation) { executeAsync(operation, TaskCallbackHandler.DUMMY_HANDLER); } public void executeAsync(final AsyncCloudTask operation, final TaskCallbackHandler callbackHandler) { operation.executeOrGetResultAsync(); myExecutingTasks.put(operation, callbackHandler); } public ScheduledFuture<?> scheduleWithFixedDelay(@NotNull final String taskName, @NotNull final Runnable task, final long initialDelay, final long delay, final TimeUnit unit){ return myExecutor.scheduleWithFixedDelay(new Runnable() { public void run() { NamedThreadFactory.executeWithNewThreadName(taskName, task); } }, initialDelay, delay, unit); } public Future<?> submit(final String taskName, final Runnable r){ return myExecutor.submit(new Runnable() { public void run() { try { LOG.debug("Starting " + taskName); NamedThreadFactory.executeWithNewThreadName(taskName, r); } finally { LOG.debug("Finished " + taskName); } } }); } private void checkTasks() { for (AsyncCloudTask task : myExecutingTasks.keySet()) { try { processSingleTask(task); } catch (Throwable th) { LOG.warnAndDebugDetails("An error occurred during checking " + task, th); } } } private void processSingleTask(AsyncCloudTask task) { final Future<CloudTaskResult> future = task.executeOrGetResultAsync(); if (future.isDone()) { final TaskCallbackHandler handler = myExecutingTasks.get(task); try { final CloudTaskResult result = future.get(); handler.onComplete(); if (result.isHasErrors()) { handler.onError(result.getThrowable()); } else { handler.onSuccess(); } } catch (Exception e) { LOG.warn(String.format("An error occurred while executing : '%s': %s", task.toString(), e.toString())); handler.onError(e); } myExecutingTasks.remove(task); if (myLongTasks.remove(task) != null) { final long operationTime = System.currentTimeMillis() - task.getStartTime(); LOG.info(String.format("Long operation finished: '%s' took %d seconds to execute", task.toString(), operationTime / 1000)); } } else { final long operationTime = System.currentTimeMillis() - task.getStartTime(); if (operationTime > LONG_TASK_TIME) { final Long lastTimeReported = myLongTasks.get(task); if (lastTimeReported == null || (System.currentTimeMillis() - lastTimeReported) > LONG_TASK_TIME) { LOG.info(String.format("Detected long running task:('%s', running for %d seconds)", task.toString(), operationTime / 1000)); myLongTasks.put(task, System.currentTimeMillis()); } } } } public void dispose(){ myExecutor.shutdown(); try { myExecutor.awaitTermination(30, TimeUnit.SECONDS); } catch (InterruptedException e) {} myExecutingTasks.clear(); } }