io.ucoin.ucoinj.elasticsearch.service.ExecutorServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for io.ucoin.ucoinj.elasticsearch.service.ExecutorServiceImpl.java

Source

package io.ucoin.ucoinj.elasticsearch.service;

/*
 * #%L
 * SIH-Adagio :: Synchro Server WebApp
 * $Id:$
 * $HeadURL:$
 * %%
 * Copyright (C) 2012 - 2014 Ifremer
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public 
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.*;
import io.ucoin.ucoinj.core.beans.InitializingBean;
import io.ucoin.ucoinj.core.exception.TechnicalException;
import io.ucoin.ucoinj.core.model.ProgressionModel;
import io.ucoin.ucoinj.core.model.ProgressionModelImpl;
import io.ucoin.ucoinj.elasticsearch.config.Configuration;
import io.ucoin.ucoinj.elasticsearch.service.task.Job;
import io.ucoin.ucoinj.elasticsearch.service.task.JobFuture;
import io.ucoin.ucoinj.elasticsearch.service.task.JobVO;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuiton.i18n.I18n;

import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static org.nuiton.i18n.I18n.l;

public class ExecutorServiceImpl implements ExecutorService, InitializingBean {

    /** Logger. */
    private static final Log log = LogFactory.getLog(ExecutorServiceImpl.class);

    private Configuration config;
    private ListeningExecutorService delegate;
    private final Map<String, JobFuture> jobsById;
    private final LoadingCache<String, ProgressionModel> progressionByJobIdCache;

    public ExecutorServiceImpl() {
        this.jobsById = Maps.newHashMap();
        this.config = Configuration.instance();
        this.progressionByJobIdCache = initJobByIdCache(config.getTaskExecutorQueueCapacity() * 2,
                config.getTaskExecutorTimeToIdle());
        delegate = MoreExecutors
                .listeningDecorator(Executors.newFixedThreadPool(config.getTaskExecutorQueueCapacity()));
    }

    @Override
    public void afterPropertiesSet() throws Exception {

    }

    @Override
    public void close() throws IOException {

        log.debug("Closing executor service");

        synchronized (jobsById) {

            for (ListenableFuture<Job> task : jobsById.values()) {
                task.cancel(true);
            }
            jobsById.clear();

            for (ProgressionModel progressionModel : progressionByJobIdCache.asMap().values()) {
                progressionModel.cancel();
            }
            progressionByJobIdCache.cleanUp();
        }

        if (!delegate.isShutdown()) {
            delegate.shutdownNow();
        }
    }

    @Override
    public ProgressionModel getProgressionByJobId(String jobId) {
        if (StringUtils.isBlank(jobId)) {
            return null;
        }
        synchronized (jobsById) {
            ListenableFuture<Job> job = jobsById.get(jobId);
            if (job != null) {
                try {
                    return job.get().getProgressionModel();
                } catch (InterruptedException | ExecutionException e) {
                    // continue
                }
            }
        }

        // Call the cache, but return null if the key is not present
        return progressionByJobIdCache.getIfPresent(jobId);
    }

    @Override
    public void execute(Runnable runnable) {
        execute(runnable, "ucoinj|job|" + System.currentTimeMillis(), null, Locale.getDefault(),
                new ProgressionModelImpl());
    }

    @Override
    public Job execute(Runnable runnable, String jobId, String issuer, Locale locale,
            ProgressionModel progression) {

        // Make sure no import already launched
        ProgressionModel existingProgression = getProgressionByJobId(jobId);
        if (existingProgression != null && existingProgression.getStatus() == ProgressionModel.Status.RUNNING) {
            throw new TechnicalException("Could not start a new synchronization: already running.");
        }

        // Run the synchro thread
        final Job job = new Job(runnable, jobId, issuer, locale, progression);

        // Execute the job
        shedule(jobId, job, l(locale, "ucoinj.task.starting"), locale);

        return job;
    }

    @Override
    public void stop(String jobId) {
        JobFuture job = jobsById.get(jobId);
        if (job != null) {
            job.cancel(false);
        }
    }

    @Override
    public List<JobVO> getAllJobs() {
        synchronized (jobsById) {
            List<JobVO> result = Lists.newArrayListWithExpectedSize(jobsById.size());
            for (Map.Entry<String, JobFuture> entry : jobsById.entrySet()) {
                String jobId = entry.getKey();
                Job job = entry.getValue().getJob();

                // System job
                String issuer = job.getIssuer();
                if (StringUtils.isBlank(issuer)) {
                    issuer = I18n.t("ucoinj.task.issuer.system");
                }

                JobVO jobVO = new JobVO(jobId, issuer);

                result.add(jobVO);
            }
            return result;
        }
    }

    public Job getJobById(String jobId) {
        synchronized (jobsById) {
            JobFuture jobFuture = jobsById.get(jobId);
            if (jobFuture != null) {
                return jobFuture.getJob();
            }
            return null;
        }
    }

    /* -- Internal methods -- */

    protected final LoadingCache<String, ProgressionModel> initJobByIdCache(final int maximumSize,
            final int expireDurationInSecond) {
        return CacheBuilder.newBuilder().maximumSize(maximumSize)
                .expireAfterWrite(expireDurationInSecond, TimeUnit.SECONDS)
                .build(new CacheLoader<String, ProgressionModel>() {
                    @Override
                    public ProgressionModel load(String jobId) throws SQLException {
                        Job job = getJobById(jobId);
                        if (job == null) {
                            throw new TechnicalException("Unknown task id");
                        }
                        return job.getProgressionModel();
                    }
                });
    }

    protected void shedule(final String jobId, final Job job, String taskMessage, final Locale locale) {

        // Set progression as as 'waiting execution'
        final ProgressionModel progressionModel = job.getProgressionModel();
        progressionModel.setTask(taskMessage);
        progressionModel.setMessage(l(locale, "ucoinj.executor.task.waitingExecution"));
        progressionModel.setStatus(ProgressionModel.Status.WAITING_EXECUTION);

        ListenableFuture<Job> future = delegate.submit(new Callable<Job>() {
            @Override
            public Job call() throws Exception {
                job.run();
                return job;
            }
        });

        JobFuture jobFuture = new JobFuture(job, future);
        Futures.addCallback(jobFuture, new FutureCallback<Job>() {
            @Override
            public void onSuccess(Job result) {
                onJobFinish(jobId, progressionModel);
            }

            @Override
            public void onFailure(Throwable t) {
                // TODO EIS : remove this (and log into the progress message instead ?)
                log.error(t);
                onJobFinish(jobId, progressionModel);
            }
        });

        // start job
        onJobScheduled(jobId, job, jobFuture);
    }

    protected void onJobScheduled(final String jobId, final Job job, final JobFuture jobFuture) {
        synchronized (jobsById) {
            jobsById.put(jobId, jobFuture);
        }
    }

    protected void onJobFinish(final String jobId, final ProgressionModel progressionModel) {
        synchronized (jobsById) {
            // Before to remove from jobsById map, put the progressionModel again into the cache
            // To avoid a getStatus() does not get retrieve it (mantis #23391)
            progressionByJobIdCache.put(jobId, progressionModel);

            // ok, progressionModel is in the cache, so remove from map
            jobsById.remove(jobId);
        }
    }
}