com.oneops.util.ReliableExecutor.java Source code

Java tutorial

Introduction

Here is the source code for com.oneops.util.ReliableExecutor.java

Source

/*******************************************************************************
 *  
 *   Copyright 2015 Walmart, Inc.
 *  
 *   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 com.oneops.util;

import static java.util.concurrent.TimeUnit.SECONDS;

import java.io.*;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.Properties;
import java.util.TreeSet;
import java.util.concurrent.*;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;

public abstract class ReliableExecutor<I> {

    protected Logger logger = LoggerFactory.getLogger(this.getClass());
    private Class<I> clazz;

    private static final String PROPERTY_CONF = "/oneops-config.properties";
    private static final String SCAN_PERIOD_PROP = "oo.antenna.client.scan.period";
    private static final String SCAN_FOLDER_PROP = "oo.antenna.client.scan.folder";

    protected long scanPeriod = 5;
    protected String scanFolder;

    protected int backlogThreshold = 1000;
    protected String name;
    protected String shortName;
    protected int threadPoolSize;
    protected boolean logExecutionErrors = true;

    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    private ThreadPoolExecutor executors;

    protected Gson gson = new Gson();

    public ReliableExecutor() {
        executors = (ThreadPoolExecutor) Executors.newCachedThreadPool();
    }

    public ReliableExecutor(int threadPoolSize) {
        this(threadPoolSize, false);
    }

    public ReliableExecutor(int threadPoolSize, boolean doSyncOnRejection) {
        this.threadPoolSize = threadPoolSize;
        RejectedExecutionHandler handler;
        if (doSyncOnRejection) {
            handler = new ThreadPoolExecutor.CallerRunsPolicy();
        } else {
            handler = new ThreadPoolExecutor.AbortPolicy();
        }
        executors = new ThreadPoolExecutor(0, threadPoolSize, 60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>(), handler);
    }

    public void setScanPeriod(int scanPeriod) {
        this.scanPeriod = scanPeriod;
    }

    public void setScanFolder(String scanFolder) {
        this.scanFolder = scanFolder;
    }

    @SuppressWarnings("unchecked")
    public void init() {
        this.checkAndCreateFolder();
        this.clazz = (Class<I>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        this.scheduler.scheduleWithFixedDelay(scanner, scanPeriod, scanPeriod, SECONDS);
    }

    public void executeAsync(I param) {
        Task task = new Task(param);
        try {
            executors.submit(task);
        } catch (RejectedExecutionException e) {
            if (logExecutionErrors) {
                logger.error("Exception while submitting task in ReliableExecutor ", e);
            }
            writeToFile(param);
        }
    }

    public boolean executeSync(I param) {
        return firstOneRun(param);
    }

    public void destroy() {
        scheduler.shutdown();
    }

    @SuppressWarnings("unused")
    private void loadPropertyConfiguration() {
        InputStream is = this.getClass().getResourceAsStream(PROPERTY_CONF);
        if (is != null) {
            Properties p = new Properties();
            try {
                p.load(is);
            } catch (IOException e) {
                e.printStackTrace();
            }

            String sp = p.getProperty(SCAN_PERIOD_PROP);
            if (sp != null) {
                this.scanPeriod = Integer.parseInt(sp);
            }
            String sf = p.getProperty(SCAN_FOLDER_PROP);
            if (sf != null) {
                this.scanFolder = sf;
            }
        }

    }

    private void checkAndCreateFolder() {
        if (this.scanFolder != null) {
            File f = new File(this.scanFolder);
            if (f.exists()) {
                if (f.isFile()) {
                    throw new RuntimeException("Given path: " + this.scanFolder + " already exists as file!");
                }
            } else {
                if (f.mkdirs()) {
                    logger.info("Folder " + this.scanFolder + " has been created successfully.");
                } else {
                    throw new RuntimeException("Given path:" + this.scanFolder + " is not valid!");
                }
            }
        } else {
            throw new RuntimeException("Scan folder path cnnot be null!");
        }
    }

    abstract protected boolean process(I param);

    final Runnable scanner = new Runnable() {
        public void run() {
            logger.trace("Scanning folder ...");
            File folder = new File(scanFolder);
            for (String fileName : new TreeSet<String>(Arrays.asList(folder.list()))) {
                File file = new File(scanFolder + File.separator + fileName);
                logger.trace("Found file : {}", file);
                try {
                    Reader reader = new FileReader(file);
                    I param = gson.fromJson(reader, clazz);
                    reader.close();
                    if (process(param)) {
                        if (!file.delete()) {
                            logger.error("File {} cannot be deleted.", file.getName());
                        }
                    } else {
                        break;
                    }
                } catch (FileNotFoundException e) {
                    logger.error("File {} not found.", file.getName());
                } catch (IOException e) {
                    logger.error(e.getMessage());
                }
            }
            logger.trace("Scanning folder finish.");
        }
    };

    private boolean firstOneRun(I param) {
        if (process(param)) {
            return true;
        }
        writeToFile(param);
        return false;
    }

    private void writeToFile(I param) {
        try {
            if (name != null) {
                logger.warn(name + " execution failed. storing data to a file.");
                logger.warn(name + " - Active workers count : " + executors.getActiveCount());
            }

            checkBacklog();
            FileWriter wr = new FileWriter(getFileName(param));
            gson.toJson(param, wr);
            wr.close();
        } catch (Exception e) {
            logger.error(e.getMessage());
            logger.debug(e.getMessage(), e);
        }
    }

    private void checkBacklog() {
        File folder = new File(scanFolder);
        String[] files = folder.list();
        if (name != null) {
            if (files.length > backlogThreshold) {
                logger.warn(name + " - retry backlog is high : " + files.length);
            }
        }
    }

    private String getFileName(I param) {
        String fileName = null;
        if (StringUtils.isEmpty(shortName)) {
            fileName = scanFolder + File.separator + System.currentTimeMillis() + String.valueOf(param.hashCode());
        } else {
            fileName = scanFolder + File.separator + shortName + "-" + System.currentTimeMillis()
                    + String.valueOf(param.hashCode());
        }
        return fileName;
    }

    class Task implements Runnable {

        private I param;

        Task(I param) {
            this.param = param;
        }

        @Override
        public void run() {
            firstOneRun(param);
        }
    }

    public void setBacklogThreshold(int backlogThreshold) {
        this.backlogThreshold = backlogThreshold;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setShortName(String shortName) {
        this.shortName = shortName;
    }

    public void setLogExecutionErrors(boolean logExecutionErrors) {
        this.logExecutionErrors = logExecutionErrors;
    }
}