Hadoop-0.20.0源代码分析(02)

UserGroupInformation类定义了一个与文件系统相关的用户和组信息抽象的内容,Hadoop框架实现了一个基于Unix系统的用户和组信息的实现类UnixUserGroupInformation,该类继承自UserGroupInformation抽象类。

从UserGroupInformation抽象类与其子类UnixUserGroupInformation的属性字段可以看出,抽象类所定义的功能信息重心在,描述一个登录以后获得的UserGroupInformation实例,而UnixUserGroupInformation类主要是侧重于登录前的信息的处理。首先看一下UnixUserGroupInformation类中定义的属性:

[java] view plain copy
  1. public static final String DEFAULT_USERNAME = "DrWho";  
  2. public static final String DEFAULT_GROUP = "Tardis";  
  3. final static private HashMap<String, UnixUserGroupInformation> user2UGIMap = new HashMap<String, UnixUserGroupInformation>();  
  4.   
  5. private String userName;  
  6. private String[] groupNames;  
  7.   
  8. final private static String UGI_TECHNOLOGY = "STRING_UGI";   

前面两个是默认的Unix用户名DEFAULT_USERNAME和组名DEFAULT_GROUP,另外其中用户名userName和组名groupNames是根据UnixUserGroupInformation类构造方法设置的,这样保证了即使无法得到用户和组的信息,也能够使用默认的值去填充,比较适合用于测试,快速定位到用户名和组名的设置处。

第二个属性user2UGIMap是一个<用户名, 用户和组信息实例>的Map,用来快速获取到用户和组的信息。

最后一个UGI_TECHNOLOGY定义读获取用户和组信息的方式,显然该类中默认使用从文本中读取字符串的方式来构造。

对于UnixUserGroupInformation类实例的构造,该类给出了四个方法:

[java] view plain copy
  1. public UnixUserGroupInformation() {  
  2. }  
  3.   
  4. public UnixUserGroupInformation(String userName, String[] groupNames) {  
  5.   setUserGroupNames(userName, groupNames);  
  6. }  
  7.   
  8. /**  
  9.  * 根据一个或多个包含了“用户名和组名”的字符串的来构造UnixUserGroupInformation实例。 
  10.  */  
  11. public UnixUserGroupInformation(String[] ugi) {  
  12.   if (ugi==null || ugi.length < 2) {  
  13.     throw new IllegalArgumentException( "Parameter does contain at least ""one user name and one group name");  
  14.   }  
  15.   
  16. public static UnixUserGroupInformation createImmutable(String[] ugi) {  
  17.   return new UnixUserGroupInformation(ugi) {  
  18.     public void readFields(DataInput in) throws IOException {  
  19.       throw new UnsupportedOperationException();  
  20.     }  
  21.   };  
  22. }  
  23.   String[] groupNames = new String[ugi.length-1];  
  24.   System.arraycopy(ugi, 1, groupNames, 0, groupNames.length);  
  25.   setUserGroupNames(ugi[0], groupNames);  
  26. }  

该类实现了其抽象基类定义的两个抽象方法,用来获取用户名和组名,如下所示:

[java] view plain copy
  1. public String[] getGroupNames() {  
  2.   return groupNames;  
  3. }  
  4.   
  5. public String getUserName() {  
  6.   return userName;  
  7. }  

UserGroupInformation抽象类实现了org.apache.hadoop.io.Writable接口,并没有在其中实现序列化操作的两个方法,所以在其子类UnixUserGroupInformationg给出了实现,如下:

[java] view plain copy
  1. /**  
  2.  * 反序列化重组(this)对象 
  3.  */  
  4. public void readFields(DataInput in) throws IOException {  
  5.   // 首先读取UGI类型,默认就是从文本读字符串的类型  
  6.   String ugiType = Text.readString(in);  
  7.   if (!UGI_TECHNOLOGY.equals(ugiType)) {  
  8.     throw new IOException("Expect UGI prefix: " + UGI_TECHNOLOGY + ", but receive a prefix: " + ugiType);  
  9.   }  
  10.     
  11.   // 从DataInput in流对象中读取用户和组的信息  
  12.   userName = Text.readString(in);  
  13.   int numOfGroups = WritableUtils.readVInt(in);  
  14.   groupNames = new String[numOfGroups];  
  15.   for (int i = 0; i < numOfGroups; i++) {  
  16.     groupNames[i] = Text.readString(in);  
  17.   }  
  18. }  
  19.   
  20. /**  
  21.  * 序列化(this)对象,写入到DataOutput out输出流中  
  22.  */  
  23. public void write(DataOutput out) throws IOException {  
  24.   // 将UGI_TECHNOLOGY写入到DataOutput out中,写入位置是字节流的最前面  
  25.   Text.writeString(out, UGI_TECHNOLOGY);  
  26.   // 写入操作  
  27.   Text.writeString(out, userName);  
  28.   WritableUtils.writeVInt(out, groupNames.length);  
  29.   for (String groupName : groupNames) {  
  30.     Text.writeString(out, groupName);  
  31.   }  
  32. }  

 下面两个方法,将用户和组的信息以字符串的形式存到Hadoop的配置类Configuration实例中,也能从一个给定的Configuration类实例中读取出来:

[java] view plain copy
  1. public static void saveToConf(Configuration conf, String attr, UnixUserGroupInformation ugi ) {  
  2.   conf.set(attr, ugi.toString()); // 通过键值对的形式存储  
  3. }  
  4.   
  5. public static UnixUserGroupInformation readFromConf(Configuration conf, String attr) throws LoginException {  
  6.   String[] ugi = conf.getStrings(attr); // 从conf中读取用户和组信息字符串  
  7.   if(ugi == null) {  
  8.     return null;  
  9.   }  
  10.   UnixUserGroupInformation currentUGI = null;  
  11.   if (ugi.length>0 ){  
  12.     currentUGI = user2UGIMap.get(ugi[0]); // 根据从配置实例中读取到的用户名,从user2UGIMap中获取一个UnixUserGroupInformation实例  
  13.   }  
  14.   if (currentUGI == null) {  
  15.     try {  
  16.       currentUGI = new UnixUserGroupInformation(ugi); // 如果user2UGIMap中不存在从conf中读取用户对应的UnixUserGroupInformation实例,直接根据字符串信息构造一个  
  17.       user2UGIMap.put(currentUGI.getUserName(), currentUGI); // 同时加入到user2UGIMap中  
  18.     } catch (IllegalArgumentException e) {  
  19.       throw new LoginException("Login failed: "+e.getMessage());  
  20.     }  
  21.   }      
  22.   return currentUGI;  
  23. }  

与执行Unix系统的Shell命令相关的三个方法,介绍如下:

[java] view plain copy
  1. /**  
  2.  * 模拟Unix系统的Shell命令whoami来获取当前用户名 
  3.  */  
  4. static String getUnixUserName() throws IOException {  
  5.   String[] result = executeShellCommand(new String[]{Shell.USER_NAME_COMMAND}); // 调用executeShellCommand()方法  
  6.   if (result.length!=1) {  
  7.     throw new IOException("Expect one token as the result of " +   
  8.         Shell.USER_NAME_COMMAND + ": " + toString(result));  
  9.   }  
  10.   return result[0];  
  11. }  
  12.   
  13. /**  
  14.  * 模拟Unix系统的Shell命令groups来获取和当前用户相关的组列表信息 
  15.  */  
  16. private static String[] getUnixGroups() throws IOException {  
  17.   return executeShellCommand(Shell.getGROUPS_COMMAND());  
  18. }  
  19.   
  20. /* 根据输入的Unix系统Shell命令,模拟Unix系统执行 */  
  21. private static String[] executeShellCommand(String[] command) throws IOException {  
  22.   String groups = Shell.execCommand(command);  
  23.   StringTokenizer tokenizer = new StringTokenizer(groups);  
  24.   int numOfTokens = tokenizer.countTokens();  
  25.   String[] tokens = new String[numOfTokens];  
  26.   for (int i=0; tokenizer.hasMoreTokens(); i++) {  
  27.     tokens[i] = tokenizer.nextToken();  
  28.   }  
  29.   return tokens;  
  30. }  

 上面三个方法能够模拟执行Unix系统的Shell命令,与Hadoop框架中实现的工具类org.apache.hadoop.util.Shell类密切相关,执行Unix系统的Shell命令的实现有一点点复杂,可以参考其工具类实现。

上面分析与登录系统之前用户和组信息的获取实现。当一个用户具备了充分的信息,可以登录文件系统进行特定的操作。下面就分析执行登录的过程了,有三个实现方法,基本原理都是一致的,下面给出这三个方法的声明及其说明: 

[java] view plain copy
  1. /**   
  2.  * 通过模拟Unix系统执行Shell命令获取用户名和组名信息,执行登录动作。 
  3.  */  
  4. public static UnixUserGroupInformation login() throws LoginException;  
  5.   
  6. /**   
  7.  * 调用readFromConf方法从conf中获取UGI; 
  8.  * 若不为UGI != null,则根据save来决定是否将UGI存储到conf中; 
  9.  * 若UGI == null,调用无参Login()方法登录,返回不为null的UGI,并根据save来决定是否将UGI存储到conf中。 
  10.  */  
  11. public static UnixUserGroupInformation login(Configuration conf, boolean save) throws LoginException;  
  12.   
  13. /**   
  14.  * 设置save = false,调用login(conf, save)执行登录动作,也就是不把用户和组信息保存到conf中。 
  15.  */  
  16. public static UnixUserGroupInformation login(Configuration conf) throws LoginException;  

 最后,该类还有一个用于比较两个UGI是否相同的方法public boolean equals(Object other)。

 总结一下,UnixUserGroupInformation类主要对登录前的用户名和组名信息进行格式化,使用两种方式来获取:

1、通过从Hadoop的配置类实例中获取到用户名和组名;

2、模拟执行Unix系统Shell命令获取到 用户名和组名。

你可能感兴趣的:(Hadoop-0.20.0)