最近,在研究公司的调度平台,也就是在某一应用程序中调用另一个应该程序,从而完成相应的业务逻辑。这里,另一应用程序是以jar包的方式提供。
下面,笔者直接贴代码,相关的解释已于注释:
/**
* Project Name:testJava
* File Name:JarInvoke.java
* Package Name:com.qiyongkang.javaJar
* Date:2015年9月18日上午9:02:58
* Copyright (c) 2015, CANNIKIN(http://http://code.taobao.org/p/cannikin/src/) All Rights Reserved.
*
*/
package com.qiyongkang.javaJar;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* ClassName:JarInvoke <br/>
* Function: TODO ADD FUNCTION. <br/>
* Reason: TODO ADD REASON. <br/>
* Date: 2015年9月18日 上午9:02:58 <br/>
*
* @author qiyongkang
* @version
* @since JDK 1.6
* @see
*/
public class JarInvoke {
/*
* 本示例是使用的java -jar方式调度程序
* 触发调用一个应用程序,1:以jar的方式运行 2:通过当前线程的contextClassLoader来加载外部jar,然后通过反射调用main方法
* jar方式表示的进程级调用,另外一种方式表示内部加载类,以线程的方式去运行
* 调用运行jar的JVM参数,如果选择以线程的方式去运行,那么这个参数将不起作用
*
* 2.类加载方式,然后通过反射
* 每个运行中的线程都有一个成员contextClassLoader,用来在运行时动态地载入其它类
* 系统默认的contextClassLoader是systemClassLoader,
* 所以一般而言java程序在执行时可以使用JVM自带的类、$JAVA_HOME/jre/lib/ext/中的类和$CLASSPATH/中的类,
* 对于非默认的jar,一般只能手动在配置环境添加。
* 但事实上,我们可以通过Thread.currentThread().setContextClassLoader()更改当前线程的contextClassLoader行为,
* 实现在程序内加载外部jar
* ClassLoader的工作原理是:
* 1) 线程需要用到某个类时,contextClassLoader将被请求来载入该类
* 2) contextClassLoader请求它的父ClassLoader来完成该载入请求
* 3) 如果父ClassLoader无法载入类,则contextClassLoader试图自己来载入
* 这种方式,在没有优化的前提下,这种直接加载外部包的速度在jvm会有很大损耗,所以并不适合直接运用在需要复杂运算的jar中类调用上
*/
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("java ");
// 添加JVM参数
sb.append("-Xms128m -Xmx256m -Xss1m ");
// 所执行的jar路径名
sb.append("-jar ");
sb.append("./ShutDown/com.qiyongkang.shutdown.ShutDown.1.0.0.jar ");
// 添加应用程序运行时传递的参数
sb.append("3600");
String cmd = sb.toString();
try {
Process process = Runtime.getRuntime().exec(cmd);
// 读取子进程中的输入流信息,必须读取流信息,否则进程运行一段时间后会被挂起而不运行!
// 先读取错误流,在读取标准流
JarInvoke jarInvoke = new JarInvoke();
jarInvoke.new ProcessStreamHelper(process.getErrorStream(), ProcessStreamHelper.ERROR_STREAM).start();
jarInvoke.new ProcessStreamHelper(process.getInputStream(), ProcessStreamHelper.NORMAL_STREAM).start();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* ClassName: ProcessStreamHelper <br/>
* 子进程流读取帮助类,用来读取进程中的流信息.
* <p/>
*
* @author qiyongkang
* @since JDK 1.6
*/
class ProcessStreamHelper extends Thread {
final static int NORMAL_STREAM = 0;
final static int ERROR_STREAM = 1;
private InputStream is;
private int streamType;
ProcessStreamHelper(InputStream is, int streamType) {
super();
this.is = is;
this.streamType = streamType;
}
@Override
public void run() {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line = null;
try {
while ((line = reader.readLine()) != null) {
line = new String(line.getBytes(), "UTF-8");
if (streamType == NORMAL_STREAM) {
// debugLog("Read Line: " + line);
} else if (streamType == ERROR_STREAM) {
errorLog("Read Error: " + line);
} else {
// debugLog("Unkown Input Stream Type[" + streamType +
// "]");
}
}
} catch (IOException e) {
errorLog("IOE: " + e + "\t\tMessage: " + e.getMessage());
}
}
void debugLog(String message) {
System.out.println("[Invoker Debug] " + message);
}
void errorLog(String message) {
System.err.println("[Invoker Error] " + message);
}
}
}
我的项目结构展示如下图:
这里我的jar包使用ant打的包,相关的Main-Class和ClassPath已在MANIFEST.MF中指定:
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.4
Created-By: 1.7.0_79-b15 (Oracle Corporation)
Main-Class: com.qiyongkang.shutdown.ShutDown
Class-Path: lib/commons-logging-1.1.3.jar lib/druid-1.0.0.jar
就写到这里了,有点偷懒,但也说得很清楚,这种调度程序的场景在某些项目中会使用到,比如数据处理,需要定期执行,因此便可打成jar包,放在统一的调度平台来调度执行这些jar程序。