hadoop-common-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dhr...@apache.org
Subject svn commit: r601038 - in /lucene/hadoop/trunk: ./ src/java/org/apache/hadoop/fs/ src/java/org/apache/hadoop/security/ src/test/org/apache/hadoop/security/
Date Tue, 04 Dec 2007 18:51:21 GMT
Author: dhruba
Date: Tue Dec  4 10:51:19 2007
New Revision: 601038

URL: http://svn.apache.org/viewvc?rev=601038&view=rev
Log:
HADOOP-2299.  Defination of a login interface.  A simple implementation for
Unix users and groups. (Hairong Kuang via dhruba)


Added:
    lucene/hadoop/trunk/src/java/org/apache/hadoop/security/
    lucene/hadoop/trunk/src/java/org/apache/hadoop/security/UnixUserGroupInformation.java
  (with props)
    lucene/hadoop/trunk/src/java/org/apache/hadoop/security/UserGroupInformation.java   (with
props)
    lucene/hadoop/trunk/src/test/org/apache/hadoop/security/
    lucene/hadoop/trunk/src/test/org/apache/hadoop/security/TestUnixUserGroupInformation.java
  (with props)
Modified:
    lucene/hadoop/trunk/CHANGES.txt
    lucene/hadoop/trunk/src/java/org/apache/hadoop/fs/ShellCommand.java

Modified: lucene/hadoop/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/hadoop/trunk/CHANGES.txt?rev=601038&r1=601037&r2=601038&view=diff
==============================================================================
--- lucene/hadoop/trunk/CHANGES.txt (original)
+++ lucene/hadoop/trunk/CHANGES.txt Tue Dec  4 10:51:19 2007
@@ -27,6 +27,9 @@
 
     HADOOP-1857.  Ability to run a script when a task fails to capture stack
     traces. (Amareshwari Sri Ramadasu via ddas)
+
+    HADOOP-2299.  Defination of a login interface.  A simple implementation for
+    Unix users and groups. (Hairong Kuang via dhruba)
     
   IMPROVEMENTS
 

Modified: lucene/hadoop/trunk/src/java/org/apache/hadoop/fs/ShellCommand.java
URL: http://svn.apache.org/viewvc/lucene/hadoop/trunk/src/java/org/apache/hadoop/fs/ShellCommand.java?rev=601038&r1=601037&r2=601038&view=diff
==============================================================================
--- lucene/hadoop/trunk/src/java/org/apache/hadoop/fs/ShellCommand.java (original)
+++ lucene/hadoop/trunk/src/java/org/apache/hadoop/fs/ShellCommand.java Tue Dec  4 10:51:19
2007
@@ -23,7 +23,9 @@
 
 /** A base class for running a unix command like du or df*/
 abstract public class ShellCommand {
-  /** a Unix command to get a list of groups */
+  /** a Unix command to get the current user's name */
+  public final static String USER_NAME_COMMAND = "whoami";
+  /** a Unix command to get the current user's groups list */
   public static final String GROUPS_COMMAND = "groups";
   /** a Unix command to set permission */
   public static final String SET_PERMISSION_COMMAND = "chmod";

Added: lucene/hadoop/trunk/src/java/org/apache/hadoop/security/UnixUserGroupInformation.java
URL: http://svn.apache.org/viewvc/lucene/hadoop/trunk/src/java/org/apache/hadoop/security/UnixUserGroupInformation.java?rev=601038&view=auto
==============================================================================
--- lucene/hadoop/trunk/src/java/org/apache/hadoop/security/UnixUserGroupInformation.java
(added)
+++ lucene/hadoop/trunk/src/java/org/apache/hadoop/security/UnixUserGroupInformation.java
Tue Dec  4 10:51:19 2007
@@ -0,0 +1,388 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.security;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import javax.security.auth.login.LoginException;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.ShellCommand;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.io.WritableUtils;
+
+/** An implementation of UserGroupInformation in the Unix system */
+public class UnixUserGroupInformation implements UserGroupInformation {
+  final static public String UGI_PROPERTY_NAME = "hadoop.job.ugi";
+  final static private HashMap<String, UnixUserGroupInformation> user2UGIMap =
+    new HashMap<String, UnixUserGroupInformation>();
+
+  private String userName;
+  private String[] groupNames;
+
+  /** Default constructor
+   */
+  public UnixUserGroupInformation() {
+  }
+
+  /** Constructor with parameters user name and its group names.
+   * The first entry in the groups list is the default  group.
+   * 
+   * @param userName a user's name
+   * @param groupNames groups list, first of which is the default group
+   * @exception IllegalArgumentException if any argument is null
+   */
+  UnixUserGroupInformation(String userName, String[] groupNames)
+  throws IOException {
+    setUserGroupNames(userName, groupNames);
+  }
+
+  /** Constructor with parameter user/group names
+   * 
+   * @param ugi an array containing user/group names, the first
+   *                     element of which is the user name, the second of
+   *                     which is the default group name.
+   * @exception IllegalArgumentException if the array size is less than 2 
+   *                                     or any element is null.
+   */
+  UnixUserGroupInformation(String[] ugi) {
+    if (ugi==null || ugi.length < 2) {
+      throw new IllegalArgumentException( "Parameter does contain at least "+
+          "one user name and one group name");
+    }
+    String[] groupNames = new String[ugi.length-1];
+    System.arraycopy(ugi, 1, groupNames, 0, groupNames.length);
+    setUserGroupNames(ugi[0], groupNames);
+  }
+  
+  /* Set this object's user name and group names
+   * 
+   * @param userName a user's name
+   * @param groupNames groups list, the first of which is the default group
+   * @exception IllegalArgumentException if any argument is null
+   */
+  private void setUserGroupNames(String userName, String[] groupNames) {
+    if (userName==null || userName.length()==0 ||
+        groupNames== null || groupNames.length==0) {
+      throw new IllegalArgumentException(
+          "Parameters should not be null or an empty string/array");
+    }
+    for (int i=0; i<groupNames.length; i++) {
+      if(groupNames[i] == null || groupNames[i].length() == 0) {
+        throw new IllegalArgumentException("A null group name at index " + i);
+      }
+    }
+    this.userName = userName;
+    this.groupNames = groupNames;
+  }
+
+  /** Return an array of group names
+   */
+  public String[] getGroupNames() {
+    return groupNames;
+  }
+
+  /** Return the user's name
+   */
+  public String getUserName() {
+    return userName;
+  }
+
+  /** Return the default  group name
+   */
+  public String getDefaultGroupName() {
+    return groupNames[0];
+  }
+
+  /* The following two methods implements Writable interface */
+  final private static String UGI_TECHNOLOGY = "STRING_UGI"; 
+  /** Deserialize this object
+   * First check if this is a UGI in the string format.
+   * If no, throw an IOException; otherwise
+   * set this object's fields by reading them from the given data input
+   *  
+   *  @param in input stream
+   *  @exception IOException is thrown if encounter any error when reading
+   */
+  public void readFields(DataInput in) throws IOException {
+    // read UGI type first
+    String ugiType = Text.readString(in);
+    if (!UGI_TECHNOLOGY.equals(ugiType)) {
+      throw new IOException("Expect UGI prefix: " + UGI_TECHNOLOGY +
+          ", but receive a prefix: " + ugiType);
+    }
+    
+    // read this object
+    userName = Text.readString(in);
+    int numOfGroups = WritableUtils.readVInt(in);
+    groupNames = new String[numOfGroups];
+    for (int i = 0; i < numOfGroups; i++) {
+      groupNames[i] = Text.readString(in);
+    }
+  }
+
+  /** Serialize this object
+   * First write a string marking that this is a UGI in the string format,
+   * then write this object's serialized form to the given data output
+   * 
+   * @param out output stream
+   * @exception IOException if encounter any error during writing
+   */
+  public void write(DataOutput out) throws IOException {
+    // write a prefix indicating the type of UGI being written
+    Text.writeString(out, UGI_TECHNOLOGY);
+    // write this object
+    Text.writeString(out, userName);
+    WritableUtils.writeVInt(out, groupNames.length);
+    for (String groupName : groupNames) {
+      Text.writeString(out, groupName);
+    }
+  }
+
+  /* The following two methods deal with transferring UGI through conf. 
+   * In this pass of implementation we store UGI as a string in conf. 
+   * Later we may change it to be a more general approach that stores 
+   * it as a byte array */
+  /** Store the given <code>ugi</code> as a comma separated string in
+   * <code>conf</code> as a property <code>attr</code>
+   * 
+   * The String starts with the user name followed by the default group names,
+   * and other group names.
+   * 
+   * @param conf configuration
+   * @param attr property name
+   * @param ugi a UnixUserGroupInformation
+   */
+  public static void saveToConf(Configuration conf, String attr, 
+      UnixUserGroupInformation ugi ) {
+    conf.set(attr, ugi.toString());
+  }
+  
+  /** Read a UGI from the given <code>conf</code>
+   * 
+   * The object is expected to store with the property name <code>attr</code>
+   * as a comma separated string that starts
+   * with the user name followed by group names.
+   * If the property name is not defined, return null.
+   * It's assumed that there is only one UGI per user. If this user already
+   * has a UGI in the ugi map, return the ugi in the map.
+   * Otherwise, construct a UGI from the configuration, store it in the
+   * ugi map and return it.
+   * 
+   * @param conf configuration
+   * @param attr property name
+   * @return a UnixUGI
+   * @throws LoginException if the stored string is ill-formatted.
+   */
+  public static UnixUserGroupInformation readFromConf(
+      Configuration conf, String attr) throws LoginException {
+    String[] ugi = conf.getStrings(attr);
+    if(ugi == null) {
+      return null;
+    }
+    UnixUserGroupInformation currentUGI = null;
+    if (ugi.length>0 ){
+      currentUGI = user2UGIMap.get(ugi[0]);
+    }
+    if (currentUGI == null) {
+      try {
+        currentUGI = new UnixUserGroupInformation(ugi);
+        user2UGIMap.put(currentUGI.getUserName(), currentUGI);
+      } catch (IllegalArgumentException e) {
+        throw new LoginException("Login failed: "+e.getMessage());
+      }
+    }
+    
+    return currentUGI;
+  }
+  
+  /* Get current user's name and the names of all its groups from Unix.
+   * It's assumed that there is only one UGI per user. If this user already
+   * has a UGI in the ugi map, return the ugi in the map.
+   * Otherwise get the current user's information from Unix, store it
+   * in the map, and return it.
+   */
+  private static UnixUserGroupInformation login() throws LoginException {
+    try {
+      String userName =  getUnixUserName();
+
+      // check if this user already has a UGI object in the ugi map
+      UnixUserGroupInformation ugi = user2UGIMap.get(userName);
+      if (ugi != null) {
+        return ugi;
+      }
+
+      /* get groups list from UNIX. 
+       * It's assumed that the first group is the default group.
+       */
+      String[]  groupNames = getUnixGroups();
+
+      // construct a Unix UGI
+      ugi = new UnixUserGroupInformation(userName, groupNames);
+      user2UGIMap.put(ugi.getUserName(), ugi);
+      return ugi;
+    } catch (Exception e) {
+      throw new LoginException("Login failed: "+e.getMessage());
+    }
+  }
+
+  /** Get a user's name & its group names from the given configuration; 
+   * If it is not defined in the configuration, get the current user's
+   * information from Unix.
+   * If the user has a UGI in the ugi map, return the one in
+   * the UGI map.
+   * 
+   *  @param conf either a job configuration or client's configuration
+   *  @return UnixUserGroupInformation a user/group information
+   *  @exception LoginException if not able to get the user/group information
+   */
+  public static UnixUserGroupInformation login(Configuration conf) 
+   throws LoginException {
+    UnixUserGroupInformation ugi = readFromConf(conf, UGI_PROPERTY_NAME);
+    if (ugi == null) {
+      ugi = login();
+    } 
+    return ugi;
+  }
+  
+  /* Return a string representation of a string array.
+   * Two strings are separated by a blank.
+   */
+  private static String toString(String[] strArray) {
+    if (strArray==null || strArray.length==0) {
+      return "";
+    }
+    StringBuilder buf = new StringBuilder(strArray[0]);
+    for (int i=1; i<strArray.length; i++) {
+      buf.append(' ');
+      buf.append(strArray[i]);
+    }
+    return buf.toString();
+  }
+  
+  /** Get current user's name from Unix by running the command whoami.
+   * 
+   * @return current user's name
+   * @throws IOException if encounter any error while running the command
+   */
+  static String getUnixUserName() throws IOException {
+    String[] result = executeShellCommand(
+        new String[]{ShellCommand.USER_NAME_COMMAND});
+    if (result.length!=1) {
+      throw new IOException("Expect one token as the result of " + 
+          ShellCommand.USER_NAME_COMMAND + ": " + toString(result));
+    }
+    return result[0];
+  }
+
+  /** Get the current user's group list from Unix by running the command groups
+   * 
+   * @return the groups list that the current user belongs to
+   * @throws IOException if encounter any error when running the command
+   */
+  private static String[] getUnixGroups() throws IOException {
+    return executeShellCommand(new String[]{ShellCommand.GROUPS_COMMAND});
+  }
+  
+  /* Execute a command and return the result as an array of Strings */
+  private static String[] executeShellCommand(String[] command)
+  throws IOException {
+    String groups = ShellCommand.execCommand(command);
+    StringTokenizer tokenizer = new StringTokenizer(groups);
+    int numOfTokens = tokenizer.countTokens();
+    String[] tokens = new String[numOfTokens];
+    for (int i=0; tokenizer.hasMoreTokens(); i++) {
+      tokens[i] = tokenizer.nextToken();
+    }
+
+    return tokens;
+  }
+
+  /** Decide if two UGIs are the same
+   *
+   * @param other other object
+   * @return true if they are the same; false otherwise.
+   */
+  public boolean equals(Object other) {
+    if (this == other) {
+      return true;
+    }
+    
+    if (!(other instanceof UnixUserGroupInformation)) {
+      return false;
+    }
+    
+    UnixUserGroupInformation otherUGI = (UnixUserGroupInformation)other;
+    
+    // check userName
+    if (userName == null) {
+      if (otherUGI.getUserName() != null) {
+        return false;
+      }
+    } else {
+      if (!userName.equals(otherUGI.getUserName())) {
+        return false;
+      }
+    }
+    
+    // checkGroupNames
+    if (groupNames == otherUGI.groupNames) {
+      return true;
+    }
+    if (groupNames.length != otherUGI.groupNames.length) {
+      return false;
+    }
+    // check default group name
+    if (groupNames.length>0 && !groupNames[0].equals(otherUGI.groupNames[0]))
{
+      return false;
+    }
+    // check all group names, ignoring the order
+    return new TreeSet<String>(Arrays.asList(groupNames)).equals(
+           new TreeSet<String>(Arrays.asList(otherUGI.groupNames)));
+  }
+
+  /** Returns a hash code for this UGI. 
+   * The hash code for a UGI is the hash code of its user name string.
+   * 
+   * @return  a hash code value for this UGI.
+   */
+  public int hashCode() {
+    return getUserName().hashCode();
+  }
+  
+  /** Convert this object to a string
+   * 
+   * @return a comma separated string containing the user name and group names
+   */
+  public String toString() {
+    StringBuilder buf = new StringBuilder();
+    buf.append(userName);
+    for (String groupName : groupNames) {
+      buf.append(',');
+      buf.append(groupName);
+    }
+    return buf.toString();
+  }
+}

Propchange: lucene/hadoop/trunk/src/java/org/apache/hadoop/security/UnixUserGroupInformation.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: lucene/hadoop/trunk/src/java/org/apache/hadoop/security/UnixUserGroupInformation.java
------------------------------------------------------------------------------
    svn:keywords = Id Revision HeadURL

Added: lucene/hadoop/trunk/src/java/org/apache/hadoop/security/UserGroupInformation.java
URL: http://svn.apache.org/viewvc/lucene/hadoop/trunk/src/java/org/apache/hadoop/security/UserGroupInformation.java?rev=601038&view=auto
==============================================================================
--- lucene/hadoop/trunk/src/java/org/apache/hadoop/security/UserGroupInformation.java (added)
+++ lucene/hadoop/trunk/src/java/org/apache/hadoop/security/UserGroupInformation.java Tue
Dec  4 10:51:19 2007
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.security;
+
+import org.apache.hadoop.io.Writable;
+
+/** A {@link Writable} interface for storing user and groups information.
+ */
+public interface UserGroupInformation extends Writable {
+  /** Get username
+   * 
+   * @return the user's name
+   */
+  public String getUserName();
+  
+  /** Get the name of the groups that the user belong to
+   * 
+   * @return an array of group names
+   */
+  public String[] getGroupNames();
+  
+  /** Get the default group name.
+   * 
+   * @return the default the group name
+   */
+  public String getDefaultGroupName();
+}

Propchange: lucene/hadoop/trunk/src/java/org/apache/hadoop/security/UserGroupInformation.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: lucene/hadoop/trunk/src/java/org/apache/hadoop/security/UserGroupInformation.java
------------------------------------------------------------------------------
    svn:keywords = Id Revision HeadURL

Added: lucene/hadoop/trunk/src/test/org/apache/hadoop/security/TestUnixUserGroupInformation.java
URL: http://svn.apache.org/viewvc/lucene/hadoop/trunk/src/test/org/apache/hadoop/security/TestUnixUserGroupInformation.java?rev=601038&view=auto
==============================================================================
--- lucene/hadoop/trunk/src/test/org/apache/hadoop/security/TestUnixUserGroupInformation.java
(added)
+++ lucene/hadoop/trunk/src/test/org/apache/hadoop/security/TestUnixUserGroupInformation.java
Tue Dec  4 10:51:19 2007
@@ -0,0 +1,103 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.security;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.io.TestWritable;
+
+import junit.framework.TestCase;
+
+/** Unit tests for UnixUserGroupInformation */
+public class TestUnixUserGroupInformation extends TestCase {
+  final private static String USER_NAME = "user1";
+  final private static String GROUP1_NAME = "group1";
+  final private static String GROUP2_NAME = "group2";
+  final private static String GROUP3_NAME = "group3";
+  final private static String[] GROUP_NAMES = 
+                      new String[]{GROUP1_NAME, GROUP2_NAME, GROUP3_NAME};
+  
+  /** Test login method */
+  public void testLogin() throws Exception {
+    Configuration conf = new Configuration();
+    
+    // loin from unix
+    String userName = UnixUserGroupInformation.getUnixUserName();
+    UnixUserGroupInformation curUserGroupInfo = 
+        UnixUserGroupInformation.login(conf);
+    assertEquals(curUserGroupInfo.getUserName(), userName);
+    assertTrue(curUserGroupInfo == UnixUserGroupInformation.login(conf));
+    
+    // login from the configuration
+    UnixUserGroupInformation userGroupInfo = new UnixUserGroupInformation(
+        USER_NAME, GROUP_NAMES );
+    UnixUserGroupInformation.saveToConf(conf, 
+        UnixUserGroupInformation.UGI_PROPERTY_NAME, userGroupInfo);
+    curUserGroupInfo = UnixUserGroupInformation.login(conf);
+    assertEquals(curUserGroupInfo, userGroupInfo);
+    assertTrue(curUserGroupInfo == UnixUserGroupInformation.login(conf));
+  }
+
+  /** test constructor */
+  public void testConstructor() throws Exception {
+    UnixUserGroupInformation uugi = 
+      new UnixUserGroupInformation(USER_NAME, GROUP_NAMES);
+    assertEquals(uugi, new UnixUserGroupInformation( new String[]{
+       USER_NAME, GROUP1_NAME, GROUP2_NAME, GROUP3_NAME} ));  
+    // failure test
+    testConstructorFailures(null, GROUP_NAMES);
+    testConstructorFailures("", GROUP_NAMES);
+    testConstructorFailures(USER_NAME, null);
+    testConstructorFailures(USER_NAME, new String[0]);
+    testConstructorFailures(USER_NAME, new String[]{null});
+    testConstructorFailures(USER_NAME, new String[]{""});
+    testConstructorFailures(USER_NAME, new String[]{GROUP1_NAME, null});
+    testConstructorFailures(USER_NAME, 
+        new String[]{GROUP1_NAME, null, GROUP2_NAME});
+  }
+  
+  private void testConstructorFailures(String userName, String[] groupNames) {
+    boolean gotException = false;
+    try {
+      new UnixUserGroupInformation(userName, groupNames);
+    } catch (Exception e) {
+      gotException = true;
+    }
+    assertTrue(gotException);
+  }
+  
+  public void testEquals() throws Exception {
+    UnixUserGroupInformation uugi = 
+      new UnixUserGroupInformation(USER_NAME, GROUP_NAMES);
+
+    assertEquals(uugi, uugi);
+    assertEquals(uugi, new UnixUserGroupInformation(USER_NAME, GROUP_NAMES));
+    assertEquals(uugi, new UnixUserGroupInformation(USER_NAME,
+        new String[]{GROUP1_NAME, GROUP3_NAME, GROUP2_NAME}));
+    assertFalse(uugi.equals(new UnixUserGroupInformation()));
+    assertFalse(uugi.equals(new UnixUserGroupInformation(USER_NAME,
+        new String[]{GROUP2_NAME, GROUP3_NAME, GROUP1_NAME})));
+  }
+  
+  /** test Writable */
+  public void testWritable() throws Exception {
+    UnixUserGroupInformation ugi = new UnixUserGroupInformation(
+        USER_NAME, GROUP_NAMES);
+    TestWritable.testWritable(ugi, new Configuration());
+  }
+}
\ No newline at end of file

Propchange: lucene/hadoop/trunk/src/test/org/apache/hadoop/security/TestUnixUserGroupInformation.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: lucene/hadoop/trunk/src/test/org/apache/hadoop/security/TestUnixUserGroupInformation.java
------------------------------------------------------------------------------
    svn:keywords = Id Revision HeadURL



Mime
View raw message