summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorIngo Lafrenz <github@der-ingo.de>2021-03-18 15:45:02 +0100
committerFlorian Zschocke <2362065+flaix@users.noreply.github.com>2021-07-05 20:13:39 +0200
commite04cad2726141653e9e2559a95a91d83ae5e4fa4 (patch)
tree3ed8b4618f2f03b2a2d5c72f278fb501fed900c2 /src
parent0910fbc59fe3dc36270bbd7f140e247e9dc7cca7 (diff)
downloadgitblit-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.java2
-rw-r--r--src/main/java/com/gitblit/StoredUserConfig.java172
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);
+ }
+
+ }
+
+}