Skip to content

Commit 5a52f6e

Browse files
authored
Fix performance problem by caching unix group and user names (fixes #109) (#135)
1 parent 412238d commit 5a52f6e

File tree

1 file changed

+57
-12
lines changed

1 file changed

+57
-12
lines changed

src/main/java/org/codehaus/plexus/components/io/attributes/FileAttributes.java

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,18 @@
2121

2222
import java.io.File;
2323
import java.io.IOException;
24+
import java.nio.file.FileSystem;
2425
import java.nio.file.Files;
2526
import java.nio.file.LinkOption;
2627
import java.nio.file.Path;
2728
import java.nio.file.attribute.FileTime;
2829
import java.nio.file.attribute.PosixFilePermission;
2930
import java.security.Principal;
3031
import java.util.Collections;
31-
import java.util.HashMap;
3232
import java.util.Map;
3333
import java.util.Set;
34+
import java.util.WeakHashMap;
35+
import java.util.concurrent.ConcurrentHashMap;
3436

3537
/*
3638
* File attributes
@@ -68,6 +70,12 @@ public class FileAttributes implements PlexusIoResourceAttributes {
6870

6971
private final FileTime lastModifiedTime;
7072

73+
private static final Map<FileSystem, Map<Integer, String>> UIDS_CACHE =
74+
Collections.synchronizedMap(new WeakHashMap<>());
75+
76+
private static final Map<FileSystem, Map<Integer, String>> GIDS_CACHE =
77+
Collections.synchronizedMap(new WeakHashMap<>());
78+
7179
/**
7280
* @deprecated use {@link #FileAttributes(File)} and remove the unused userCache and groupCache parameters
7381
*/
@@ -79,32 +87,64 @@ public FileAttributes(
7987
}
8088

8189
public FileAttributes(@Nonnull File file) throws IOException {
82-
this(file, false);
90+
this(file.toPath(), false);
8391
}
8492

8593
public FileAttributes(@Nonnull File file, boolean followLinks) throws IOException {
94+
this(file.toPath(), followLinks);
95+
}
96+
97+
private static Map<Integer, String> getUserCache(FileSystem fs) {
98+
return UIDS_CACHE.computeIfAbsent(fs, f -> new ConcurrentHashMap<>());
99+
}
100+
101+
private static Map<Integer, String> getGroupCache(FileSystem fs) {
102+
return GIDS_CACHE.computeIfAbsent(fs, f -> new ConcurrentHashMap<>());
103+
}
104+
105+
public FileAttributes(@Nonnull Path path, boolean followLinks) throws IOException {
86106
LinkOption[] options = followLinks ? FOLLOW_LINK_OPTIONS : NOFOLLOW_LINK_OPTIONS;
87-
Path path = file.toPath();
88107
Set<String> views = path.getFileSystem().supportedFileAttributeViews();
89108
String names;
90109
if (views.contains("unix")) {
91-
names = "unix:*";
110+
names =
111+
"unix:gid,uid,isSymbolicLink,isRegularFile,isDirectory,isOther,mode,permissions,size,lastModifiedTime";
92112
} else if (views.contains("posix")) {
93113
names = "posix:*";
94114
} else {
95115
names = "basic:*";
96116
}
97117
Map<String, Object> attrs = Files.readAttributes(path, names, options);
98-
if (!attrs.containsKey("group") && !attrs.containsKey("owner") && views.contains("owner")) {
99-
Map<String, Object> ownerAttrs = Files.readAttributes(path, "owner:*", options);
100-
Map<String, Object> newAttrs = new HashMap<>(attrs);
101-
newAttrs.putAll(ownerAttrs);
102-
attrs = newAttrs;
103-
}
104118
this.groupId = (Integer) attrs.get("gid");
105-
this.groupName = attrs.containsKey("group") ? ((Principal) attrs.get("group")).getName() : null;
119+
if (attrs.containsKey("group")) {
120+
this.groupName = ((Principal) attrs.get("group")).getName();
121+
} else if (this.groupId != null) {
122+
Map<Integer, String> cache = getGroupCache(path.getFileSystem());
123+
String name = cache.get(this.groupId);
124+
if (name == null) {
125+
name = getPrincipalName(path, "unix:group");
126+
cache.put(this.groupId, name);
127+
}
128+
this.groupName = name;
129+
} else {
130+
this.groupName = null;
131+
}
106132
this.userId = (Integer) attrs.get("uid");
107-
this.userName = attrs.containsKey("owner") ? ((Principal) attrs.get("owner")).getName() : null;
133+
if (attrs.containsKey("owner")) {
134+
this.userName = ((Principal) attrs.get("owner")).getName();
135+
} else if (this.userId != null) {
136+
Map<Integer, String> cache = getUserCache(path.getFileSystem());
137+
String name = cache.get(this.userId);
138+
if (name == null) {
139+
name = getPrincipalName(path, "unix:owner");
140+
cache.put(this.userId, name);
141+
}
142+
this.userName = name;
143+
} else if (views.contains("owner")) {
144+
this.userName = getPrincipalName(path, "owner:owner");
145+
} else {
146+
this.userName = null;
147+
}
108148
this.symbolicLink = (Boolean) attrs.get("isSymbolicLink");
109149
this.regularFile = (Boolean) attrs.get("isRegularFile");
110150
this.directory = (Boolean) attrs.get("isDirectory");
@@ -120,6 +160,11 @@ public FileAttributes(@Nonnull File file, boolean followLinks) throws IOExceptio
120160
this.lastModifiedTime = (FileTime) attrs.get("lastModifiedTime");
121161
}
122162

163+
private static String getPrincipalName(Path path, String attribute) throws IOException {
164+
Object owner = Files.getAttribute(path, attribute, LinkOption.NOFOLLOW_LINKS);
165+
return ((Principal) owner).getName();
166+
}
167+
123168
public FileAttributes(
124169
@Nullable Integer userId,
125170
String userName,

0 commit comments

Comments
 (0)