com.heimuheimu.runningtask.task.service.support.SimpleTaskExecutor.java Source code

Java tutorial

Introduction

Here is the source code for com.heimuheimu.runningtask.task.service.support.SimpleTaskExecutor.java

Source

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 heimuheimu
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.heimuheimu.runningtask.task.service.support;

import java.io.Closeable;
import java.lang.reflect.Method;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

import javax.annotation.PreDestroy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.heimuheimu.runningtask.task.Task;
import com.heimuheimu.runningtask.task.TaskVersion;
import com.heimuheimu.runningtask.task.service.TaskExecutor;
import com.heimuheimu.runningtask.task.service.TaskService;
import com.heimuheimu.runningtask.task.service.TaskVersionService;
import com.heimuheimu.runningtask.task.service.URLClassLoaderFactory;

/**
 * ?
 * <p>????
 *
 * @author heimuheimu
 * @date 20141125
 * @ThreadSafe
 */
@Service("simpleTaskExecutor")
public class SimpleTaskExecutor implements TaskExecutor, Closeable {

    private static final Logger LOG = LoggerFactory.getLogger(SimpleTaskExecutor.class);

    /**
     * ???MapKeyIDValue??
     */
    private HashMap<Integer, TaskVersion> taskVersionHolder = new HashMap<Integer, TaskVersion>();

    /**
     * ??ClassLoaderMapKeyID
     * ValueClassLoader
     */
    private HashMap<Integer, URLClassLoader> classLoaderHolder = new HashMap<Integer, URLClassLoader>();

    @Autowired
    private URLClassLoaderFactory classLoaderFactory;

    @Autowired
    private TaskService taskService;

    @Autowired
    private TaskVersionService taskVersionService;

    @Override
    public synchronized void run(int taskId, int version) {
        long startTime = System.currentTimeMillis();
        Task task = taskService.getTask(taskId);
        if (task == null) {
            throw new IllegalArgumentException(
                    "Task is not exist. Task id: `" + taskId + "`. Version: `" + version + "`");
        }
        if (version < 0 || task.getVersionCount() < version) {
            throw new IllegalArgumentException(
                    "Version out of bounds. Invalid version: `" + version + "`. Task info: `" + task + "`");
        }
        if (!isRunning(taskId, version)) {
            stop(taskId);
            TaskVersion taskVersion = taskVersionService.get(taskId, version);
            if (taskVersion == null) {
                throw new IllegalArgumentException(
                        "Task version is not exist. It should not be happend. Invalid version: `" + version
                                + "`. Task info: `" + task + "`");
            }
            URLClassLoader classLoader = classLoaderFactory.create(taskVersion.isUseParentClassLoader(),
                    taskVersion.getJarPath());
            boolean isSuccess = executeStaticMethod(taskVersion.getMainClass(), taskVersion.getStartMethod(),
                    classLoader);
            if (isSuccess) {
                taskVersionHolder.put(taskId, taskVersion);
                classLoaderHolder.put(taskId, classLoader);
                taskService.updateState(taskId, Task.STATE_RUNNING);
                LOG.info("Start task success. Task id: `{}`. Cost: `{}ms`. Task version info: `{}`",
                        new Object[] { taskId, System.currentTimeMillis() - startTime, taskVersion });
            } else {
                taskService.updateState(taskId, Task.STATE_START_FAIL);
                closeClassLoader(classLoader);
                LOG.error("Start task fail. Task id: `{}`. Cost: `{}ms`. Task version info: `{}`",
                        new Object[] { taskId, System.currentTimeMillis() - startTime, taskVersion });
            }
        }
    }

    @Override
    public synchronized void stop(int taskId) {
        long startTime = System.currentTimeMillis();
        TaskVersion taskVersion = taskVersionHolder.remove(taskId);
        if (taskVersion != null) {
            URLClassLoader classLoader = classLoaderHolder.remove(taskId);
            boolean isSuccess = executeStaticMethod(taskVersion.getMainClass(), taskVersion.getStopMethod(),
                    classLoader);
            closeClassLoader(classLoader);
            if (isSuccess) {
                taskService.updateState(taskId, Task.STATE_STOP);
                LOG.info("Stop task success. Task id: `{}`. Cost: `{}ms`. Task version info: `{}`",
                        new Object[] { taskId, System.currentTimeMillis() - startTime, taskVersion });
            } else {
                taskService.updateState(taskId, Task.STATE_STOP_FAIL);
                LOG.error("Stop task fail. Task id: `{}`. Cost: `{}ms`. Task version info: `{}`",
                        new Object[] { taskId, System.currentTimeMillis() - startTime, taskVersion });
            }
        }
    }

    @PreDestroy
    @Override
    public synchronized void close() {
        ArrayList<Integer> taskIdList = new ArrayList<Integer>(taskVersionHolder.keySet());
        for (int taskId : taskIdList) {
            stop(taskId);
        }
    }

    private synchronized boolean isRunning(int taskId, int version) {
        TaskVersion taskVersion = taskVersionHolder.get(taskId);
        return taskVersion != null && taskVersion.getVersion() == version;
    }

    private synchronized boolean executeStaticMethod(String className, String methodName,
            URLClassLoader classLoader) {
        long startTime = System.currentTimeMillis();
        try {
            Class<?> clz = classLoader.loadClass(className);
            Method method = clz.getMethod(methodName);
            method.invoke(null);
            LOG.info("Execute `{}#{}()` success. Cost: `{}ms`",
                    new Object[] { className, methodName, System.currentTimeMillis() - startTime });
            return true;
        } catch (Exception e) {
            LOG.error("Execute `" + className + "#" + methodName + "()` fail. Cost: `"
                    + (System.currentTimeMillis() - startTime) + "ms`", e);
            return false;
        }
    }

    private synchronized void closeClassLoader(URLClassLoader targetClassLoader) {
        if (targetClassLoader != null) {
            long startTime = System.currentTimeMillis();
            try {
                targetClassLoader.close();
                LOG.info("Close url class loader success. Cost: `{}ms`. Urls: `{}`",
                        System.currentTimeMillis() - startTime, Arrays.toString(targetClassLoader.getURLs()));
            } catch (Exception e) {
                LOG.error("Close url class loader fail. It may be lead to memory leak. Urls: `"
                        + Arrays.toString(targetClassLoader.getURLs()) + "`", e);
            }
        }
    }

}