【转】分析Hadoop自带WordCount例子的执行过程(3)

本文介绍Hadoop中如何使用GenericOptionsParser解析通用参数,并通过OptionBuilder构造Option对象,最终设置Configuration对象。

继续向下看:

 Option fs = OptionBuilder.withArgName("local|namenode:port")
    .hasArg()
    .withDescription(
"specify a namenode")
    .create(
"fs");
     opts.addOption(fs);

有一个很重要的类OptionBuilder,它才完成了充实一个Option的过程,然后经过多次调用,会将多个Option都添加到opts列表中。

看一看OptionBuilder类的withArgName()方法:

 /**
     * The next Option created will have the specified argument value 
     * name.
     *
     * 
@param name the name for the argument value
     * 
@return the OptionBuilder instance
     
*/
    
public static OptionBuilder withArgName(String name)
    {
        OptionBuilder.argName 
= name;
        
return instance;
    }

上面,为一个OptionBuilder的实例指定一个参数(argName)name,实际上是返回了一个具有nameOptionBuilder实例。

然后,又调用了hasArg()方法,它也是OptionBuilder类的静态方法:

  /**
     * The next Option created will require an argument value.
     *
     * 
@return the OptionBuilder instance
     
*/
    
public static OptionBuilder hasArg()
    {
        OptionBuilder.numberOfArgs 
= 1;
        
return instance;
    }

为刚才指定参数名的那个OptionBuilder实例设置了参数的个数,因为第一次设置,当然个数为1了。

调用withDescription()方法来设定描述信息:

 /**
     * The next Option created will have the specified description
     *
     * 
@param newDescription a description of the Option's purpose
     * 
@return the OptionBuilder instance
     
*/
    
public static OptionBuilder withDescription(String newDescription)
    {
        OptionBuilder.description 
= newDescription;
        
return instance;
    }

比较关键的是最后一步调用,通过调用OptionBuilder类的create()方法才真正完成了一个Option的创建:

 /**
     * Create an Option using the current settings and with 
     * the specified Option <code>char</code>.
     *
     * 
@param opt the <code>java.lang.String</code> representation 
     * of the Option
     * 
@return the Option instance
     * 
@throws IllegalArgumentException if <code>opt</code> is not
     * a valid character. See Option.
     
*/
    
public static Option create(String opt)
                         
throws IllegalArgumentException
    {
        
// create the option
        Option option = new Option(opt, description);

        
// set the option properties
        option.setLongOpt(longopt);
        option.setRequired(required);
        option.setOptionalArg(optionalArg);
        option.setArgs(numberOfArgs);
        option.setType(type);
        option.setValueSeparator(valuesep);
        option.setArgName(argName);
        option.setArgPattern(argPattern, limit);

        
// reset the OptionBuilder properties
        OptionBuilder.reset();
        
// return the Option instance
        return option;
    }

从上面一个Option的设置,我们可以看出来,OptionBuilder类其实是一个辅助工具,用来收集与一个Option相关的信息,从而将这些信息一次全部赋予到一个新建的Option对象上,这个对象现在具有详细的信息了。

接着,通过CommandLineParser parserparse方法,可以知道public abstract class Parser implements CommandLineParser,从抽象类Parser中找到parse的实现:

public CommandLine parse(Options options, String[] arguments, 
                             
boolean stopAtNonOption)
        
throws ParseException
    {
        
return parse(options, arguments, null, stopAtNonOption);
    }

参数stopAtNonOption表明,如果解析过程中遇到的是一个空选项是否仍然继续解析。从前面parseGeneralOptions方法 commandLine = parser.parse(opts, args, true);可知:我们传递过来一个true

再次调用Parser类的重载成员方法parse(),如下所示,解析过程非常详细:

/**
     * Parse the arguments according to the specified options and
     * properties.
     *
     * 
@param options the specified Options
     * 
@param arguments the command line arguments
     * 
@param properties command line option name-value pairs
     * 
@param stopAtNonOption stop parsing the arguments when the first
     * non option is encountered.
     *
     * 
@return the list of atomic option and value tokens
     *
     * 
@throws ParseException if there are any problems encountered
     * while parsing the command line tokens.
     
*/
    
public CommandLine parse(Options options, String[] arguments, 
                             Properties properties, 
boolean stopAtNonOption)
        
throws ParseException
    {
        
// initialise members
        this.options = options;
        requiredOptions 
= options.getRequiredOptions();
        cmd 
= new CommandLine();
        
boolean eatTheRest = false;
        
if (arguments == null)
        {
            arguments 
= new String[0];
        }
        List tokenList 
= Arrays.asList(flatten(this.options, 
                                               arguments, 
                                               stopAtNonOption));
        ListIterator iterator 
= tokenList.listIterator();
       
// process each flattened token
        while (iterator.hasNext())
        {
            String t 
= (String) iterator.next();
            
// the value is the double-dash
            if ("--".equals(t))
            {
                eatTheRest 
= true;
            }
            
// the value is a single dash
            else if ("-".equals(t))
            {
                
if (stopAtNonOption)
                {
                    eatTheRest 
= true;
                }
                
else
                {
                    cmd.addArg(t);
                }
            }
            
// the value is an option
            else if (t.startsWith("-"))
            {
                
if (stopAtNonOption && !options.hasOption(t))
                {
                    eatTheRest 
= true;
                    cmd.addArg(t);
                }
                
else
                {
                    processOption(t, iterator);
                }
            }
            
// the value is an argument
            else
            {
                cmd.addArg(t);
                
if (stopAtNonOption)
                {
                    eatTheRest 
= true;
                }
            }
            
// eat the remaining tokens
            if (eatTheRest)
            {
                
while (iterator.hasNext())
                {
                    String str 
= (String) iterator.next();
                    
// ensure only one double-dash is added
                    if (!"--".equals(str))
                    {
                        cmd.addArg(str);
                    }
                }
            }
        }
        processProperties(properties);
        checkRequiredOptions();
        
return cmd;
    }

解析之后,返回CommandLine类的实例,从而GenericOptionsParser类的成员变量commandLine获取到了一个引用。commandLineGenericOptionsParser类的一个私有成员变量。

看一下CommandLine类的实现:

package org.apache.commons.cli;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/** 
* Represents list of arguments parsed against
* a {
@link Options} descriptor.
*
* It allows querying of a boolean {
@link #hasOption(String opt)},
* in addition to retrieving the {
@link #getOptionValue(String opt)}
* for options requiring arguments.
*/
public class CommandLine {
    
// 不能识别的 options/arguments 
    private List args = new LinkedList();
    
/** the processed options */
    
private Map options = new HashMap();
    
/** the option name map */
    
private Map names = new HashMap();
    
/** Map of unique options for ease to get complete list of options */
    
private Map hashcodeMap = new HashMap();
    
/** the processed options */
    
private Option[] optionsArray;
     
// 创建一个命令行CommandLine的实例。
    CommandLine()
    {
        
// nothing to do
    }
    
// 从options这个HashMap中查看,判断是否opt已经被设置了
    public boolean hasOption(String opt)
    {
        
return options.containsKey(opt);
    }
     
// 调用hasOption()方法,从options这个HashMap中查看,判断是否opt已经被设置了
    public boolean hasOption(char opt)
    {
        
return hasOption(String.valueOf(opt));
    }
    
// 根据String opt返回Option的Object类型
    public Object getOptionObject(String opt)
    {
        String res 
= getOptionValue(opt);
        
if (!options.containsKey(opt))
        {
            
return null;
        }
        Object type 
= ((Option) options.get(opt)).getType();
        
return (res == null)        ? null : TypeHandler.createValue(res, type);
    }
     
// 根据char opt返回Option的Object类型
    public Object getOptionObject(char opt)
    {
        
return getOptionObject(String.valueOf(opt));
    }
    
// 根据指定的String opt获取Option的值
    public String getOptionValue(String opt)
    {
        String[] values 
= getOptionValues(opt);
        
return (values == null? null : values[0];
    }
     
// 根据指定的char opt获取Option的值
    public String getOptionValue(char opt)
    {
        
return getOptionValue(String.valueOf(opt));
    }
    
/** 
     * Retrieves the array of values, if any, of an option.
     *
     * 
@param opt string name of the option
     * 
@return Values of the argument if option is set, and has an argument,
     * otherwise null.
     
*/
    
public String[] getOptionValues(String opt)
    {
        opt 
= Util.stripLeadingHyphens(opt);
        String key 
= opt;
        
if (names.containsKey(opt))
        {
            key 
= (String) names.get(opt);
        }
        
if (options.containsKey(key))
        {
            
return ((Option) options.get(key)).getValues();
        }
        
return null;
    }
     
// 根据指定的String opt,返回Option的值的一个数组
    public String[] getOptionValues(char opt)
    {
        
return getOptionValues(String.valueOf(opt));
    }
     
// 根据指定的String opt和String defaultValue获取Option的值
    public String getOptionValue(String opt, String defaultValue)
    {
        String answer 
= getOptionValue(opt);
        
return (answer != null? answer : defaultValue;
    }
     
// 根据指定的char opt和String defaultValue获取Option的值
    public String getOptionValue(char opt, String defaultValue)
    {
        
return getOptionValue(String.valueOf(opt), defaultValue);
    }
     
// 返回不能够解析的Option和参数的一个数组
    public String[] getArgs()
    {
        String[] answer 
= new String[args.size()];
        args.toArray(answer);
        
return answer;
    }
      
// 返回不能够解析的Option和参数的一个列表
    public List getArgList()
    {
        
return args;
    }
    
/** 
     * jkeyes
     * - commented out until it is implemented properly
     * <p>Dump state, suitable for debugging.</p>
     *
     * 
@return Stringified form of this object
     
*/
    
public String toString() {
        StringBuffer buf 
= new StringBuffer();
            
        buf.append(
"[ CommandLine: [ options: ");
        buf.append(options.toString());
        buf.append(
" ] [ args: ");
        buf.append(args.toString());
        buf.append(
" ] ]");
            
        
return buf.toString();
    }
    
/**
     * Add left-over unrecognized option/argument.
     *
     * 
@param arg the unrecognised option/argument.
     
*/
    
void addArg(String arg)
    {
        args.add(arg);
    }
   
// 向CommandLine中添加一个Option,其中Option的值(可能多个)被存储
    void addOption(Option opt)
    {
        hashcodeMap.put(
new Integer(opt.hashCode()), opt);
        String key 
= opt.getKey();
        
if (key == null)
        {
            key 
= opt.getLongOpt();
        }
        
else
        {
            names.put(opt.getLongOpt(), key);
        }
        options.put(key, opt);
    }
     
// 返回CommandLine的Option 成员表的一个迭代器
    public Iterator iterator()
    {
        
return hashcodeMap.values().iterator();
    }
    
// 返回处理过的Option的对象数组
    public Option[] getOptions()
    {
        Collection processed 
= options.values();

        
// reinitialise array
        optionsArray = new Option[processed.size()];
        
// return the array
        return (Option[]) processed.toArray(optionsArray);
    }
}

一个CommandLine中包含一个重要的HashMap,里面存储的是键值对,即(key, opt),通过它可以非常方便地设置和访问。

接着在parseGeneralOptions方法中调用processGeneralOptions()方法,进行处理:

processGeneralOptions(conf, commandLine);

 

 processGeneralOptions的处理过程如下:

 /**
   * Modify configuration according user-specified generic options
   * 
@param conf Configuration to be modified
   * 
@param line User-specified generic options
   
*/
private void processGeneralOptions(Configuration conf,
      CommandLine line) {
    
if (line.hasOption("fs")) {
      conf.set(
"fs.default.name", line.getOptionValue("fs"));
    }
    
if (line.hasOption("jt")) {
      conf.set(
"mapred.job.tracker", line.getOptionValue("jt"));
    }
    
if (line.hasOption("conf")) {
      conf.addResource(
new Path(line.getOptionValue("conf")));
    }
    
if (line.hasOption('D')) {
      String[] property 
= line.getOptionValues('D');
      
for(int i=0; i<property.length-1; i=i+2) {
        
if (property[i]!=null)
          conf.set(property[i], property[i
+1]);
      }
    }
}

传进去一个CommandLine实例,通过CommanLine的信息,来设置Configuration conf对象。设置Configuration conf对象的目的是:为HadoopTool工作而设置的,比如WordCount这个工具,在运行开始时需要获取到Hadoop的配置信息的,这个 就需要从这里设置的Configuration conf对象来获取。

上面这个processGeneralOptions()方法,是根据CommanLine的对象,获取到所有参数值的一个数组,并返回。

到此位置,前面都是为了初始化一个GenericOptionsParser parser解析器所做的工作:

GenericOptionsParser parser = new GenericOptionsParser(conf, args);

进而,可以使用 GenericOptionsParser类的实例parser 来获取Hadoop的通用参数了:

  //get the args w/o generic hadoop args
    String[] toolArgs = parser.getRemainingArgs();

已经具备了运行Hadoop工具的条件了,可以启动了:

     return tool.run(toolArgs);

可以根据返回的状态码检查工具运行情况。

上面Tool tool就是我们实例化的WordCount对象,这时候才进入到WordCount实现中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值