定时任务管理实现

热部署定时任务管理实现(上传jar+动态加载jar中接口类文件+hessian远程调用执行业务)
1.新增定时任务
主要是配置定时任务的执行周期,涉及到热部署的就是关于接口jar上传,要调用的远程接口类名称,接口中要被调用的方法名称、远程调用的hessianURL,这样子就配置好了一个定时任务,信息存放到数据库,这样就成功添加了一个定时任务
用到了loadJar这个方法,这里用到了动态加载,刚上传的jar文件要被动态的加载到JVM中,使用了URLClassLoader根据类名称读取jar中指定的类,并且返回加载相应jar的这个URLClassLoader,因为后面用到这个加载的类的时候,必须要用加载它的类加载器才行,否则会报ClassNotFoundException
 

Java代码 

 收藏代码

  1. import java.io.File;  
  2. import java.io.FileInputStream;  
  3. import java.io.FileNotFoundException;  
  4. import java.io.FileOutputStream;  
  5. import java.io.IOException;  
  6. import java.net.URL;  
  7. import java.net.URLClassLoader;  
  8. import org.apache.struts2.ServletActionContext;  
  9. public class JarUtil {  
  10. //    public final static String PATH = ServletActionContext.getServletContext().getRealPath("/")+"uploadJar"+File.separator;  
  11.     public final static String PATH = "d:/uploadJar"+File.separator;  
  12.     private static void uploadJar(File jar,String filePathName) {  
  13.         try {  
  14.             FileInputStream fin = new FileInputStream(jar);//获得上传的jar文件  
  15.             File dir = new File(PATH);  
  16.             if(!dir.exists()){  
  17.                 dir.mkdir();  
  18.             }  
  19.             FileOutputStream fout = new FileOutputStream(new File(filePathName));//输出jar到指定位置  
  20.             byte[] buffer = new byte[1024];  
  21.             int count = fin.read(buffer);  
  22.             while (count > 0) {  
  23.                 fout.write(buffer,0,count);//这里一定要标注0,count,否则jar实际上是有问题的。导致后面的jar无法加载  
  24.                 count = fin.read(buffer);  
  25.             }  
  26.              fout.flush();  
  27.              fout.close();  
  28.              fin.close();  
  29.         } catch (FileNotFoundException e) {  
  30.             e.printStackTrace();  
  31.         } catch (IOException e) {  
  32.             e.printStackTrace();  
  33.         }  
  34.     }  
  35.     /** 
  36.      *  
  37.      * @param jar 需要加载的jar文件 
  38.      * @param jarFileName jar文件的名称 
  39.      * @param className 需要加载的jar中的class的二进制名称 
  40.      * @return 
  41.      */  
  42.     public static URLClassLoader loadJar(File jar,String jarFileName,String className){  
  43.         String filePathName = PATH + jarFileName;    //得到服务器上要放置的文件路径,给jar添加一个加入时刻防止名称重复  
  44.         uploadJar(jar,filePathName);//上传jar  
  45.           
  46.         return loadClassOfJar(filePathName,className) ;//加载jar中的执行class文件  
  47.            
  48.     }  
  49.       
  50.     /** 
  51.      *  
  52.      * @param url 指向指定jar文件的URL 
  53.      * @param cls 加载jar文件中的class文件的二进制名称 
  54.      * @return 加载这个class对象的classloader 
  55.      */  
  56.     public static URLClassLoader loadClassOfJar(String filePathName, String cls) {  
  57.         URLClassLoader loader = null;  
  58.         try {  
  59.             File file = new File(filePathName);//加载这个刚上传的jar文件  
  60.             if(file != null){  
  61.                 URL url = file.toURI().toURL();  
  62.                 loader = new URLClassLoader(new URL[] { url },Thread.currentThread().getContextClassLoader());  
  63.                 loader.loadClass(cls); // 动态装载class  
  64.             }  
  65.         } catch (Exception ex) {  
  66.             ex.printStackTrace();  
  67.         }  
  68.         return loader;  
  69.     }  
  70.       
  71. }  

 
这就是web加载的时候就要启动所有已经添加的定时任务

这里要注意的是因为上传的jar文件是在一个特定的地方,不在war包中,需要启动的时候去动态加载,首先从数据库读出之前配置的定时任务,然后就是使用jarUtil的loadClassOfJar,把之前上传的jar文件里面的指定类加载,并把加载该类的相应的类加载器classLoader放到jobDetail的jobDataMap中,后面定时任务的执行的时候会用到,这里要注意这个TempleTask类,它是一个模板,只要是通过上传jar包来进行配置的定时任务都用这个高度抽象的模板,它就时一个job

Java代码 

 收藏代码

  1. import java.net.URLClassLoader;  
  2. import java.text.ParseException;  
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5. import org.quartz.CronTrigger;  
  6. import org.quartz.JobDataMap;  
  7. import org.quartz.JobDetail;  
  8. import org.quartz.Scheduler;  
  9. import org.quartz.SchedulerException;  
  10. import com.tt.TimeTaskArgDao;  
  11. import com.tt.model.TimeTaskArg;  
  12. import com.tt.TempleTask;  
  13. /** 
  14. * 初始化trigger 
  15. * @author tongwenhuan 
  16. */  
  17. public class SchedulerInit {  
  18.     private Scheduler scheduler;  
  19.     private TimeTaskArgDao timeTaskArgDao;  
  20.       
  21.     public void init() {  
  22.         try {  
  23.             String[] groupNames = scheduler.getJobGroupNames();  
  24.             for(String groupName : groupNames) {  
  25.                 String[] jobNames = scheduler.getJobNames(groupName);  
  26.                 if(jobNames.length > 0){  
  27.                     List jns = new ArrayList();  
  28.                     for(String jobName : jobNames) {  
  29.                         jns.add(jobName);  
  30.                     }  
  31.                     List tta_list = timeTaskArgDao.listTimeTaskArgByJobName(jns);  
  32.                     for(TimeTaskArg tta : tta_list) {  
  33.                         CronTrigger cronTrigger = new CronTrigger(tta.getTriggerName(),groupName,tta.getJobName(),groupName,tta.getCronexpression());      
  34.                         scheduler.scheduleJob(cronTrigger);  
  35.                         if(!tta.getTriggerStatus().equals("0")) {  
  36.                             scheduler.pauseTrigger(tta.getTriggerName(), tta.getGroupName());  
  37.                         }  
  38.                     }  
  39.                 }      
  40.             }  
  41.             List list = timeTaskArgDao.getAutoTask();  
  42.               
  43.             for(TimeTaskArg tta : list) {  
  44.                   
  45.                 String filePathName = JarUtil.PATH + tta.getJarName();//获取指定到该jar的文件路径  
  46.                 URLClassLoader loader = JarUtil.loadClassOfJar(filePathName, tta.getClassName());  //动态加载jar中的接口  
  47.                 if(loader != null){  
  48.                     JobDetail jobDetail = new JobDetail(tta.getJobName(),tta.getGroupName(),TempleTask.class);  
  49.                     JobDataMap jm = new JobDataMap();  
  50.                     jm.put("hessianUrl", tta.getHessianUrl());  
  51.                     jm.put("methodName", tta.getMethodName());  
  52.                     jm.put("methodName", tta.getMethodName());  
  53.                     jm.put("classLoaderName", loader);//每个上传的jar包对应各自的一个classLoader,绑定各自的job  
  54.                     jobDetail.setJobDataMap(jm);  
  55.                       
  56.                     CronTrigger cronTrigger = new CronTrigger(tta.getTriggerName(),tta.getGroupName(),tta.getJobName(),tta.getGroupName(),tta.getCronexpression());  
  57.                     scheduler.scheduleJob(jobDetail,cronTrigger);  
  58.                     if(!tta.getTriggerStatus().equals("0")) {  
  59.                         scheduler.pauseTrigger(tta.getTriggerName(), tta.getGroupName());  
  60.                     }  
  61.                 }  
  62.             }  
  63.               
  64.               
  65.         } catch (SchedulerException e) {  
  66.             e.printStackTrace();  
  67.         } catch (ParseException e) {  
  68.             e.printStackTrace();  
  69.         }  
  70.     }  
  71.   
  72.     public Scheduler getScheduler() {  
  73.         return scheduler;  
  74.     }  
  75.     public void setScheduler(Scheduler scheduler) {  
  76.         this.scheduler = scheduler;  
  77.     }  
  78.   
  79.     public TimeTaskArgDao getTimeTaskArgDao() {  
  80.         return timeTaskArgDao;  
  81.     }  
  82.   
  83.     public void setTimeTaskArgDao(TimeTaskArgDao timeTaskArgDao) {  
  84.         this.timeTaskArgDao = timeTaskArgDao;  
  85.     }  
  86. }  

 

Java代码 

 收藏代码

  1. import java.net.URLClassLoader;  
  2. import org.quartz.JobDataMap;  
  3. import org.quartz.JobDetail;  
  4. import org.quartz.JobExecutionContext;  
  5. import org.quartz.JobExecutionException;  
  6. import org.springframework.scheduling.quartz.QuartzJobBean;  
  7. import com.caucho.hessian.client.HessianProxyFactory;  
  8. public class TempleTask extends QuartzJobBean {  
  9.     @Override  
  10.     protected void executeInternal(JobExecutionContext context)  
  11.             throws JobExecutionException {  
  12.         try {  
  13.             HessianProxyFactory factory = new HessianProxyFactory();   
  14.             JobDetail jb = context.getJobDetail();  
  15.               
  16.             JobDataMap jm = jb.getJobDataMap();  
  17.             String hessianUrl = (String) jm.get("hessianUrl");  
  18.             String methodName = (String) jm.get("methodName");  
  19.             URLClassLoader loader = (URLClassLoader)jm.get("classLoaderName");//获取加载这个class的loader  
  20.             ClassLoader systemClassLoader = Thread.currentThread().getContextClassLoader();//记住系统当前的webAppClassLoader  
  21.             Thread.currentThread().setContextClassLoader(loader);//临时设置加载当前任务class的类加载器为当前线程的加载器,否则无法找到类  
  22.             factory.setReadTimeout(50000);  
  23.             Object o  = factory.create(hessianUrl);  
  24.             o.getClass().getMethod(methodName).invoke(o,null);  
  25.             Thread.currentThread().setContextClassLoader(systemClassLoader);//定时任务执行完之后,重新归还当前线程为系统的线程  
  26.         }catch(Exception e) {  
  27.             e.printStackTrace();  
  28.         }  
  29.           
  30.     }  
  31.       
  32. }  
  33.    

 
这个是一个公共的job模板,也就是定时任务执行的时候要真正调用的任务,这个任务没有写在本地,而是用了远程调用,使用的是hessian的代理工厂,根据上传jar包填的hessianURL就可以找到接口,也就是返回的object o,根据方法名通过反射调用业务方法,从而实现执行定时任务
这里要注意的问题是:之前放到jobDataMap中的classLoader这里起重大作用了,因为这个接口class类对象是由自定义的URLClassLoader来加载的,而此时该线程的classLoader为webAppClassLoader,是自定义的URLClassLoader的父加载器,根据全盘委托原则,webAppClassLoader会先向上去寻找这个类,一级一级往上找,没有的话才自己的范围下来找这个类,因为不是它加载的,所以他找不到,会报ClassNotFoundException的错误,此时把该线程的classLoader改为加载这个类的加载器的时候,这个加载器就可以在他自己的范围内找到这个Class,可以顺利的对这个Class对象实例化,从而返回Object对象,让后面可以反射调用(看似简单的代码中往往有很大的学问!)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值