View Javadoc

1   package com.atlassian.sal.core.features;
2   
3   import com.atlassian.sal.api.features.DarkFeatureManager;
4   import com.google.common.base.Functions;
5   import com.google.common.base.Predicate;
6   import com.google.common.collect.Collections2;
7   import com.google.common.collect.ImmutableSet;
8   import com.google.common.collect.Maps;
9   import com.google.common.collect.Sets;
10  
11  import java.io.File;
12  import java.io.FileReader;
13  import java.io.IOException;
14  import java.util.Collection;
15  import java.util.Collections;
16  import java.util.HashMap;
17  import java.util.HashSet;
18  import java.util.Map;
19  import java.util.Properties;
20  import java.util.Set;
21  
22  public class SystemDarkFeatureInitializer {
23      /**
24       * Get the dark features enabled and disabled on startup. Reads from system properties and from a "atlassian-darkfeatures.properties" file.
25       * File name can be overridden with darkfeatures.properties.file system property.
26       *
27       * @return A SystemDarkFeatures containing two sets of strings: one containing enabled dark feature keys and one containing those disabled.
28       */
29      public static SystemDarkFeatures getSystemStartupDarkFeatures() {
30          final String propertiesFile = System.getProperty(DarkFeatureManager.DARKFEATURES_PROPERTIES_FILE_PROPERTY, DarkFeatureManager.DARKFEATURES_PROPERTIES_FILE_PROPERTY_DEFAULT);
31  
32          final String disableAllPropertiesFlag = System.getProperty(DarkFeatureManager.DISABLE_ALL_DARKFEATURES_PROPERTY);
33          if (Boolean.parseBoolean(disableAllPropertiesFlag)) {
34              return SystemDarkFeatures.disableAll();
35          }
36  
37          final Set<String> enabledPropertyDarkFeatures = getPropertyDarkFeaturesWithValue(true);
38          final Set<String> enabledPropertiesFileDarkFeatures = getPropertiesFileDarkFeatures(propertiesFile, true);
39  
40          final Set<String> disabledPropertyDarkFeatures = getPropertyDarkFeaturesWithValue(false);
41          final Set<String> disabledPropertiesFileDarkFeatures = getPropertiesFileDarkFeatures(propertiesFile, false);
42  
43          // System properties override property file
44          Set<String> enabled = Sets.union(Sets.difference(enabledPropertiesFileDarkFeatures, disabledPropertyDarkFeatures), enabledPropertyDarkFeatures);
45          Set<String> disabled = Sets.union(Sets.difference(disabledPropertiesFileDarkFeatures, enabledPropertyDarkFeatures), disabledPropertyDarkFeatures);
46  
47          return SystemDarkFeatures.darkFeatures(enabled, disabled);
48      }
49  
50      private static Set<String> getPropertiesFileDarkFeatures(String filename, boolean b) {
51          final File propertiesFile = new File(filename);
52  
53          Properties properties = new Properties();
54          try {
55              properties.load(new FileReader(propertiesFile));
56          } catch (IOException e) {
57              return Collections.emptySet();
58          }
59  
60          return new HashSet<String>(getKeysForBooleanValue(filterOnAndStripKeyPrefix(properties), b));
61      }
62  
63      private static Set<String> getPropertyDarkFeaturesWithValue(boolean b) {
64          return new HashSet<String>(getKeysForBooleanValue(filterOnAndStripKeyPrefix(System.getProperties()), b));
65      }
66  
67      private static Collection<String> getKeysForBooleanValue(final Map<Object, Object> properties, final Boolean b) {
68          return Collections2.transform(Maps.filterValues(properties, new Predicate<Object>() {
69              @Override
70              public boolean apply(Object input) {
71                  // don't want to match things with neither true nor false - should be ignored
72                  return input.toString().toLowerCase().matches(b.toString());
73              }
74          }).keySet(), Functions.toStringFunction());
75      }
76  
77      private static Map<Object, Object> filterOnAndStripKeyPrefix(Map<Object, Object> properties) {
78          final Set<Map.Entry<Object, Object>> entries = Maps.filterKeys(properties, new Predicate<Object>() {
79              @Override
80              public boolean apply(Object entry) {
81                  return entry.toString().startsWith(DarkFeatureManager.ATLASSIAN_DARKFEATURE_PREFIX);
82              }
83          }).entrySet();
84  
85          Map<Object, Object> prefixStrippedEntries = new HashMap<Object, Object>();
86          for (Map.Entry<Object, Object> entry : entries) {
87              prefixStrippedEntries.put(entry.getKey().toString().substring(DarkFeatureManager.ATLASSIAN_DARKFEATURE_PREFIX.length()), entry.getValue());
88          }
89  
90          return prefixStrippedEntries;
91      }
92  
93      public static class SystemDarkFeatures {
94          private final Set<String> enabled;
95          private final Set<String> disabled;
96          private final boolean disableAll;
97  
98          private SystemDarkFeatures(Set<String> enabled, Set<String> disabled, boolean disableAll) {
99              this.enabled = ImmutableSet.copyOf(enabled);
100             this.disabled = ImmutableSet.copyOf(disabled);
101             this.disableAll = disableAll;
102         }
103 
104         public static SystemDarkFeatures disableAll() {
105             return new SystemDarkFeatures(Collections.<String>emptySet(), Collections.<String>emptySet(), true);
106         }
107 
108         public static SystemDarkFeatures darkFeatures(Set<String> enabled, Set<String> disabled) {
109             return new SystemDarkFeatures(enabled, disabled, false);
110         }
111 
112         public Set<String> getEnabled() {
113             return enabled;
114         }
115 
116         public Set<String> getDisabled() {
117             return disabled;
118         }
119 
120         public boolean isDisableAll() {
121             return disableAll;
122         }
123     }
124 }