读程序就要从main函数读起,从而了解程序的流程。所以我首先从CliDriver这个类开始。
一 ,主函数
public static void main(String[] args) throws Exception {
int ret = new CliDriver().run(args);
System.exit(ret);
}
- String[] args 是运行的时候由外部命令穿进来的参数;
- System.exit(ret)明显是退出函数
/**
* Terminates the currently running Java Virtual Machine. The
* argument serves as a status code; by convention, a nonzero status
* code indicates abnormal termination.
* <p>
* This method calls the <code>exit</code> method in class
* <code>Runtime</code>. This method never returns normally.
* <p>
* The call <code>System.exit(n)</code> is effectively equivalent to
* the call:
* <blockquote><pre>
* Runtime.getRuntime().exit(n)
* </pre></blockquote>
*
* @param status exit status.
* @throws SecurityException
* if a security manager exists and its <code>checkExit</code>
* method doesn't allow exit with the specified status.
* @see java.lang.Runtime#exit(int)
*/
public static void exit(int status) {
Runtime.getRuntime().exit(status);
}
这个是System.exit()的源码,翻译它第一段的注释-“终止当前正在运行的java虚拟机。这个参数作为状态码;按惯例,非零状态代码表示异常终止。与return不同,return只是退回到上一层,exit将会退出到最外层。
3. 最重要的一句就是建立新的CliDriver对象,并调用他的run()函数,下来我们进入类的构造函数。
二,构造函数
public CliDriver() {
SessionState ss = SessionState.get();
conf = (ss != null) ? ss.getConf() : new Configuration();
Log LOG = LogFactory.getLog("CliDriver");
if (LOG.isDebugEnabled()) {
LOG.debug("CliDriver inited with classpath " + System.getProperty("java.class.path"));
}
console = new LogHelper(LOG);
}
首先创建了一个SessionState类,并调用get()函数; SessionState封装了一个会话的关联的数据,包括配置信息HiveConf,输入输出流,指令类型,用户名称、IP地址等等。SessionState 是一个与线程关联的静态本地变量ThreadLocal,任何一个线程都对应一个SessionState,能够在Hive代码的任何地方获取到(大量被使用到),以返回用户相关或者配置信息等。来看源码:
/**
get the current session.
*/
public static SessionState get() {
return tss.get().state;
}
/**
* Singleton Session object per thread.
*
**/
private static ThreadLocal<SessionStates> tss = new ThreadLocal<SessionStates>() {
@Override
protected SessionStates initialValue() {
return new SessionStates();
}
};
注释中说get()是在获取当前会话,返回了一个对象tss的调用,有关tss建立代码我也贴出来了。有关Threadlocal详见:http://blog.csdn.net/lufeng20/article/details/24314381/ 也就是说,Threadlocal 是为了解决多线程当中变量访问冲突的问题,它为每一个线程均拷贝了一份副本,线程可以对此副本进行随意的修改而不会与其他线程发生冲突。其中get()方法返回当前线程所对应的线程局部变量,其初始值为null;
在定义好conf和log之后;System.getProperty 可以获得可执行.class文件的路径以及引入的jar包路径。LogHelper 目前看来就是一个管理日志的类;
在此提出一个问题,为什么在工程中要使用log呢?有什么帮助呢?
三,run()函数
OptionsProcessor oproc = new OptionsProcessor();
if (!oproc.process_stage1(args)) {
return 1;
}
建立OptionsProcessor类的对象oproc,并且对命令进行初步的解析,提取-e -h hiveconf hivevar等参数信息,设置用户提供的系统和Hive环境变量。具体的细节可以在OptionsProcessor这个类中查看到。
boolean logInitFailed = false;
String logInitDetailMessage;
try {
logInitDetailMessage = LogUtils.initHiveLog4j();
} catch (LogInitializationException e) {
logInitFailed = true;
logInitDetailMessage = e.getMessage();
}
初始化Log4j日志组件;使用的日志的好处是什么呢?
CliSessionState ss = new CliSessionState(new HiveConf(SessionState.class));
ss.in = System.in;
try {
ss.out = new PrintStream(System.out, true, "UTF-8");
ss.info = new PrintStream(System.err, true, "UTF-8");
ss.err = new CachingPrintStream(System.err, true, "UTF-8");
} catch (UnsupportedEncodingException e) {
return 3;
}
使用HiveConf实例化CliSessionState,CliSessionState 继承了SessionState类,,创建了输入输出流,也创建了一些记录用户输入的字符串,比如:fileName,cmdProperties,在实例化的过程中,主要是用来记录HiveConf。
if (!oproc.process_stage2(ss)) {
return 2;
}
以下是它在OptionsProcessor类中的详细定义:
public boolean process_stage2(CliSessionState ss) {
ss.getConf();
if (commandLine.hasOption('H')) {
printUsage();
return false;
}
ss.setIsSilent(commandLine.hasOption('S'));
ss.database = commandLine.getOptionValue("database");
ss.execString = commandLine.getOptionValue('e');
ss.fileName = commandLine.getOptionValue('f');
ss.setIsVerbose(commandLine.hasOption('v'));
String[] initFiles = commandLine.getOptionValues('i');
if (null != initFiles) {
ss.initFiles = Arrays.asList(initFiles);
}
if (ss.execString != null && ss.fileName != null) {
System.err.println("The '-e' and '-f' options cannot be specified simultaneously");
printUsage();
return false;
}
if (commandLine.hasOption("hiveconf")) {
Properties confProps = commandLine.getOptionProperties("hiveconf");
for (String propKey : confProps.stringPropertyNames()) {
ss.cmdProperties.setProperty(propKey, confProps.getProperty(propKey));
}
}
return true;
}
从这里可以看出,process_stage1只是对命令行进行了一个初步的解析,在process_stage2中对CliSessionState的成员进行了赋值,但这和process-stage1有什么联系吗?不应该执行完process_stage1这个函数就return了吗?程序是不断循环执行的吗?
if (!ss.getIsSilent()) {
if (logInitFailed) {
System.err.println(logInitDetailMessage);
} else {
SessionState.getConsole().printInfo(logInitDetailMessage);
}
}
在允许打印的模式下,如果初始化日志失败就打印错误信息。
HiveConf conf = ss.getConf();
for (Map.Entry<Object, Object> item : ss.cmdProperties.entrySet()) {
conf.set((String) item.getKey(), (String) item.getValue());
ss.getOverriddenConfigurations().put((String) item.getKey(), (String) item.getValue());
}
将用户命令行输入的配置信息和变量等覆盖HiveConf的默认值
prompt = conf.getVar(HiveConf.ConfVars.CLIPROMPT);
prompt = new VariableSubstitution().substitute(conf, prompt);
prompt2 = spacesForString(prompt);
注释中说:read prompt configuration and substitute variables.也就是读取配置提示和替换变量。这一步有什么作用呢?
SessionState.start(ss);
try {
return executeDriver(ss, conf, oproc);
} finally {
ss.close();
}
}
设置会话状态,然后执行CLI程序,即excuteDriver().