diff options
author | Ingo Lafrenz <github@der-ingo.de> | 2021-03-18 15:45:02 +0100 |
---|---|---|
committer | Florian Zschocke <2362065+flaix@users.noreply.github.com> | 2021-07-05 20:13:39 +0200 |
commit | e04cad2726141653e9e2559a95a91d83ae5e4fa4 (patch) | |
tree | 3ed8b4618f2f03b2a2d5c72f278fb501fed900c2 /src | |
parent | 0910fbc59fe3dc36270bbd7f140e247e9dc7cca7 (diff) | |
download | gitblit-e04cad2726141653e9e2559a95a91d83ae5e4fa4.tar.gz gitblit-e04cad2726141653e9e2559a95a91d83ae5e4fa4.zip |
bugfix: fix CPU hog bug in config save
Diffstat (limited to 'src')
-rw-r--r-- | src/main/java/com/gitblit/ConfigUserService.java | 2 | ||||
-rw-r--r-- | src/main/java/com/gitblit/StoredUserConfig.java | 172 |
2 files changed, 173 insertions, 1 deletions
diff --git a/src/main/java/com/gitblit/ConfigUserService.java b/src/main/java/com/gitblit/ConfigUserService.java index 025b1d8c..22d1c190 100644 --- a/src/main/java/com/gitblit/ConfigUserService.java +++ b/src/main/java/com/gitblit/ConfigUserService.java @@ -676,7 +676,7 @@ public class ConfigUserService implements IUserService { // Write a temporary copy of the users file
File realmFileCopy = new File(realmFile.getAbsolutePath() + ".tmp");
- StoredConfig config = new FileBasedConfig(realmFileCopy, FS.detect());
+ StoredUserConfig config = new StoredUserConfig(realmFileCopy); // write users
for (UserModel model : users.values()) {
diff --git a/src/main/java/com/gitblit/StoredUserConfig.java b/src/main/java/com/gitblit/StoredUserConfig.java new file mode 100644 index 00000000..2161ebb2 --- /dev/null +++ b/src/main/java/com/gitblit/StoredUserConfig.java @@ -0,0 +1,172 @@ +package com.gitblit; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.function.Function; + +/** + * Simple class with the only purpose to save the realm file (users.conf) in + * a fast efficient manner. The JGit Config classes used previously caused + * a massive CPU hog if the users file got bigger than about 30000 lines. + * + * @author Ingo Lafrenz + * + */ +public class StoredUserConfig { + + private final File realmFileCopy; + private SortedMap<String, Section> sections = new TreeMap<>(); + + public StoredUserConfig(File realmFileCopy) { + this.realmFileCopy = realmFileCopy; + } + + public void setString(final String section, final String subsection, String name, String value) { + Section s = sections.computeIfAbsent(generateKey(section, subsection), new Function<String, Section>() { + @Override + public Section apply(String k) { + return new Section(section, subsection); + } + }); + s.addEntry(name, value); + } + + public void setBoolean(String section, String subsection, String name, boolean value) { + setString(section, subsection, name, String.valueOf(value)); + } + + public void setStringList(String section, String subsection, String name, List<String> list) { + for (String value : list) { + setString(section, subsection, name, value); + } + } + + public void save() throws IOException { + try (FileWriter fileWriter = new FileWriter(realmFileCopy); + PrintWriter printWriter = new PrintWriter(fileWriter);) { + for (Map.Entry<String,Section> entry : sections.entrySet()) { + writeSection(printWriter, entry.getKey(), entry.getValue()); + } + } + } + + private static void writeSection(PrintWriter printWriter, String key, Section section) { + printWriter.printf("[%s \"%s\"]\n", section.getName(), section.getSubSection()); + for (Entry entry : section.getEntries().values()) { + writeEntry(printWriter, entry.getKey(), entry.getValue()); + } + } + + private static void writeEntry(PrintWriter printWriter, String key, String value) { + printWriter.printf("\t%s = %s\n", key, escape(value)); + } + + private static String escape(String value) { + String fixedValue = '#' == value.charAt(0) ? "\"" + value + "\"" : value; + fixedValue = fixedValue.replace("\\", "\\\\"); + return fixedValue; + } + + private static String generateKey(String key, String subKey) { + return "k:" + key + "s:" + subKey; + } + + private static class Section { + private final String name; + private final String subSection; + private final SortedMap<String, Entry> entries = new TreeMap<>(); + + public Section(String name, String subSection) { + this.name = name; + this.subSection = subSection; + } + + public void addEntry(final String key, final String value) { + entries.put(generateKey(key, value), new Entry(key, value)); + } + + public String getName() { + return name; + } + + public String getSubSection() { + return subSection; + } + + public SortedMap<String, Entry> getEntries() { + return entries; + } + + @Override + public int hashCode() { + return Objects.hash(name, subSection); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Section other = (Section) obj; + return Objects.equals(name, other.name) && Objects.equals(subSection, other.subSection); + } + + @Override + public String toString() { + return String.format("Section [name=%s, subSection=%s]", name, subSection); + } + + } + + private static class Entry { + private final String key; + private final String value; + + public Entry(String key, String value) { + this.key = key; + this.value = value; + } + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + @Override + public int hashCode() { + return Objects.hash(key, value); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Entry other = (Entry) obj; + return Objects.equals(key, other.key) && Objects.equals(value, other.value); + } + + @Override + public String toString() { + return String.format("Entry [key=%s, value=%s]", key, value); + } + + } + +} |