Java tutorial
/* * 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); } } } }