I have been scouring the Internet looking for a Java package/class that will allow me to parse the UNIX /etc/group file. While it really wouldn't be so hard to write this from scratch, I'm quite surprised not to find something already out there. There is a POSIX passwd class (see http://www.bmsi.com/java/posix/docs/posix.Passwd.html), but I'm not finding a similar class for /etc/group. Does such a thing exist?
Java is machine independent then I don't get surprised for this. The class you refer is not standard.
I think that the same reason that gives to hide the /etc/group file from everyone, can give us a clue for understand the reason why java don't provide it.
Heres my code that tofubeer updated that I updated again. His didn't compile. missing InvalidGroupException class. Also, no package was specified. Switched EMPTY_LIST to emptyList() to avoid lack of parameterization.
package fileutils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class GroupReader2 {
public static class InvalidGroupException extends Exception {
private static final long serialVersionUID = 1L;
public InvalidGroupException(String string) {
super(string);
}
}
public static GroupReader2 parseGroup(final String groupLine)
throws InvalidGroupException {
final String line;
final String[] parts;
if (groupLine == null) {
throw new IllegalArgumentException("groupLine cannot be null");
}
line = groupLine.trim();
if (line.startsWith("#") || line.isEmpty()) {
return null;
}
parts = line.split(":");
if (parts.length < 3) {
throw new InvalidGroupException(groupLine
+ "must be in the format of name:passwd:gid[:userlist]");
}
try {
final GroupReader2 group;
final String name;
final String passwd;
final int gid;
final List<String> userList;
name = parts[0];
passwd = parts[1];
gid = Integer.parseInt(parts[2]);
if (parts.length == 4) {
userList = Arrays.asList(parts[3].split(","));
} else {
userList = Collections.emptyList();
}
group = new GroupReader2(name, passwd, gid, userList);
return group;
} catch (final NumberFormatException ex) {
throw new InvalidGroupException(groupLine + " gid must be a number");
}
}
private final int gid;
private final String name;
private final String passwd;
private final List<String> userList;
public GroupReader2(final String nm, final String pw, final int id,
final List<String> users) {
name = nm;
passwd = pw;
gid = id;
userList = Collections.unmodifiableList(new ArrayList<String>(users));
}
public int getGid() {
return (gid);
}
public String getName() {
return (name);
}
public String getPasswd() {
return (passwd);
}
public List<String> getUserList() {
return (userList);
}
@Override
public String toString() {
final StringBuilder sb;
sb = new StringBuilder();
sb.append(name);
sb.append(":");
sb.append(passwd);
sb.append(":");
sb.append(gid);
sb.append(":");
for (final String user : userList) {
sb.append(user);
sb.append(",");
}
sb.setLength(sb.length() - 1);
return (sb.toString());
}
}
In case you are going to run this program anywhere except your own machine, reading /etc/group is not a nice idea.
The real-world uses nis/ldap etc to keep all this info..
You may want to wrap getpwent / getgrent family with JNI You may get some posix implementation of java which wraps these.
Hope that helps..
Here is the code John Ellinwood provided, but made safer (edit: added Johns changes, slightly differently, to keep then in sync with the comments. Good to see how two people implement the same sort of code).
I chose to throws exceptions in the case of an invalid line, you could simply return null as he originally did (I don't see the point in using a file that has wrong data...).
The only "must do" change it wrapping the userList as an UnmodifableList (or returning a new copy of the list) otherwise a malicious user of this method could add things to the userList (call getUserList and then proceed to add items to it or remove items from it).
Since the Group class is immutable (all instance variables are final) there is no fear of them being changed by a caller.
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Group
{
private final int gid;
private final String name;
private final String passwd;
private final List<String> userList;
public static Group parseGroup(final String groupLine)
throws InvalidGroupException
{
final String line;
final String[] parts;
if(groupLine == null)
{
throw new IllegalArgumentException("groupLine cannot be null");
}
line = groupLine.trim();
if(line.startsWith("#") || line.isEmpty())
{
return null;
}
parts = line.split(":");
if(parts.length < 3)
{
throw new InvalidGroupException(groupLine + "must be in the format of name:passwd:gid[:userlist]", line);
}
try
{
final Group group;
final String name;
final String passwd;
final int gid;
final List<String> userList;
name = parts[0];
passwd = parts[1];
gid = Integer.parseInt(parts[2]);
if(parts.length == 4)
{
userList = Arrays.asList(parts[3].split(","));
}
else
{
userList = Collections.emptyList();
}
group = new Group(name, passwd, gid, userList);
return group;
}
catch(final NumberFormatException ex)
{
throw new InvalidGroupException(groupLine + " gid must be a number", line);
}
}
public Group(final String nm, final String pw, final int id, final List<String> users)
{
name = nm;
passwd = pw;
gid = id;
userList = Collections.unmodifiableList(new ArrayList<String>(users));
}
public int getGid()
{
return (gid);
}
public String getName()
{
return (name);
}
public String getPasswd()
{
return (passwd);
}
public List<String> getUserList()
{
return (userList);
}
@Override
public String toString()
{
final StringBuilder sb;
sb = new StringBuilder();
sb.append(name);
sb.append(":");
sb.append(passwd);
sb.append(":");
sb.append(gid);
sb.append(":");
for(final String user : userList)
{
sb.append(user);
sb.append(",");
}
sb.setLength(sb.length() - 1);
return (sb.toString());
}
}
public class InvalidGroupException
extends Exception
{
private static final long serialVersionUID = 1L;
private final String line;
public InvalidGroupException(final String msg, final String ln)
{
super(msg);
line = ln;
}
public String getLine()
{
return (line);
}
}