package mil.dds.anet.database;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Provider;
import mil.dds.anet.beans.AdminSetting;
import mil.dds.anet.database.mappers.AdminSettingMapper;
import org.jdbi.v3.core.Handle;
import ru.vyarus.guicey.jdbi3.tx.InTransaction;

public class AdminDao {

  public static enum AdminSettingKeys {
    SECURITY_BANNER_TEXT, SECURITY_BANNER_COLOR, DEFAULT_APPROVAL_ORGANIZATION, HELP_LINK_URL,
    CONTACT_EMAIL, DAILY_ROLLUP_MAX_REPORT_AGE_DAYS, EXTERNAL_DOCUMENTATION_LINK_URL,
    EXTERNAL_DOCUMENTATION_LINK_TEXT, GENERAL_BANNER_LEVEL, GENERAL_BANNER_TEXT,
    GENERAL_BANNER_VISIBILITY,
  }

  @Inject
  private Provider<Handle> handle;
  private Map<String, String> cachedSettings = null;

  protected Handle getDbHandle() {
    return handle.get();
  }

  private void initCache() {
    cachedSettings = new HashMap<String, String>();
    List<AdminSetting> settings = getAllSettings();
    for (AdminSetting s : settings) {
      cachedSettings.put(s.getKey(), s.getValue());
    }
  }

  public String getSetting(AdminSettingKeys key) {
    if (cachedSettings == null) {
      initCache();
    }
    return cachedSettings.get(key.toString());
  }

  @InTransaction
  public List<AdminSetting> getAllSettings() {
    return getDbHandle().createQuery("/* getAllAdminSettings */ SELECT * FROM \"adminSettings\"")
        .map(new AdminSettingMapper()).list();
  }

  /**
   * Saves an adminSetting to the database, inserting if it does not exist yet.
   */
  @InTransaction
  public int saveSetting(AdminSetting setting) {
    if (cachedSettings == null) {
      initCache();
    }
    String sql;
    if (cachedSettings.containsKey(setting.getKey())) {
      sql =
          "/* updateAdminSetting */ UPDATE \"adminSettings\" SET value = :value WHERE \"key\" = :key";
    } else {
      sql =
          "/* insertAdminSetting */ INSERT INTO \"adminSettings\" (\"key\", value) VALUES (:key, :value)";
    }
    cachedSettings.put(setting.getKey(), setting.getValue());
    return getDbHandle().createUpdate(sql).bind("key", setting.getKey())
        .bind("value", setting.getValue()).execute();
  }

  @InTransaction
  public void updateMaterializedView(String viewName) {
    // Can't use a prepared statement with a parameter here, alas
    getDbHandle()
        .execute(String.format("REFRESH MATERIALIZED VIEW CONCURRENTLY \"%1$s\"", viewName));
  }
}

@@ -792,16 +792,81 @@
  = null;
+%0A  private static final Object cachedSettingsLock = new Object();
 %0A%0A  prot
@@ -961,112 +961,140 @@
     
-cachedSettings = new HashMap%3CString, String%3E();%0A    List%3CAdminSetting%3E settings = getAllSettings
+synchronized (cachedSettingsLock) %7B%0A      if (cachedSettings == null) %7B%0A        cachedSettings = new HashMap%3CString, String%3E
 ();%0A    
 for 
@@ -1093,13 +1093,23 @@
     
+    
 for (
+final 
 Admi
@@ -1125,16 +1125,24 @@
 s : 
-s
+getAllS
 ettings
+()
 ) %7B%0A
@@ -1143,24 +1143,28 @@
 ()) %7B%0A      
+    
 cachedSettin
@@ -1193,24 +1193,42 @@
 etValue());%0A
+        %7D%0A      %7D%0A
     %7D%0A  %7D%0A%0A 
@@ -1281,44 +1281,8 @@
 ) %7B%0A
-    if (cachedSettings == null) %7B%0A  
     
@@ -1290,30 +1290,24 @@
 nitCache();%0A
-    %7D%0A
     return c
@@ -1722,44 +1722,8 @@
 ) %7B%0A
-    if (cachedSettings == null) %7B%0A  
     
@@ -1739,21 +1739,21 @@
 ();%0A    
-%7D%0A   
+final
  String 
@@ -1757,28 +1757,72 @@
 ng sql;%0A    
-if
+synchronized
  (
+cachedSettingsLock) %7B%0A      sql = 
 cachedSettin
@@ -1857,33 +1857,20 @@
 y())
-) %7B
 %0A      
-sql =%0A     
     
+?
  %22/*
@@ -1961,44 +1961,20 @@
 key%22
-;
 %0A    
-%7D else %7B%0A      sql =%0A   
       
+:
  %22/*
@@ -2069,20 +2069,16 @@
 ue)%22;%0A  
-  %7D%0A
     cach
@@ -2127,24 +2127,30 @@
 etValue());%0A
+    %7D%0A
     return g

Fix possible race condition on the cached admin settings