View Javadoc
1   package com.atlassian.refapp.sal.usersettings;
2   
3   import com.atlassian.fugue.Option;
4   import com.atlassian.plugin.spring.scanner.annotation.export.ExportAsService;
5   import com.atlassian.refapp.sal.FileBackedSettingsFileFactory;
6   import com.atlassian.refapp.sal.FileBackedSettingsService;
7   import com.atlassian.sal.api.ApplicationProperties;
8   import com.atlassian.sal.api.user.UserKey;
9   import com.atlassian.sal.api.user.UserManager;
10  import com.atlassian.sal.api.usersettings.UserSettings;
11  import com.atlassian.sal.api.usersettings.UserSettingsBuilder;
12  import com.atlassian.sal.api.usersettings.UserSettingsService;
13  import com.atlassian.sal.core.usersettings.DefaultUserSettings;
14  import com.google.common.annotations.VisibleForTesting;
15  import com.google.common.base.Function;
16  import com.google.common.collect.ImmutableSet;
17  import org.apache.log4j.Logger;
18  
19  import java.util.Set;
20  import javax.inject.Inject;
21  import javax.inject.Named;
22  
23  import static com.google.common.base.Preconditions.checkNotNull;
24  
25  @ExportAsService
26  @Named("userSettingsService")
27  public class RefimplUserSettingsService extends FileBackedSettingsService implements UserSettingsService {
28      private static final Logger log = Logger.getLogger(RefimplUserSettingsService.class);
29  
30      private final UserManager userManager;
31  
32      @Inject
33      public RefimplUserSettingsService(UserManager userManager, ApplicationProperties applicationProperties) {
34          super(FileBackedSettingsFileFactory.getFileAndProperties("sal.com.atlassian.refapp.sal.usersettings.store", "com.atlassian.refapp.sal.usersettings.xml", Boolean.valueOf(System.getProperty("sal.com.atlassian.refapp.sal.usersettings.usememorystore", "false")), applicationProperties));
35          this.userManager = userManager;
36      }
37  
38      @VisibleForTesting
39      RefimplUserSettingsService(UserManager userManager) {
40          this.userManager = userManager;
41      }
42  
43      @Override
44      public UserSettings getUserSettings(String username) {
45          if (userManager.resolve(username) == null) {
46              throw new IllegalArgumentException("No user exists with username " + username);
47          }
48  
49          SettingsMap settingsMap = new SettingsMap(username);
50          UserSettingsBuilder settings = UserSettingsBuilderRefAppImpl.builder();
51          for (String key : settingsMap.keySet()) {
52              final String value = settingsMap.get(key);
53              if (value.startsWith("B")) {
54                  settings.put(key, Boolean.parseBoolean(value.substring(1)));
55              } else if (value.startsWith("L")) {
56                  settings.put(key, Long.parseLong(value.substring(1)));
57              } else if (value.startsWith("S")) {
58                  settings.put(key, value.substring(1));
59              } else {
60                  log.warn("Found user setting with unknown prefix. User: " + username + ", key: " + key + ", value: " + value);
61              }
62          }
63  
64          return settings.build();
65      }
66  
67      @Override
68      public UserSettings getUserSettings(UserKey userKey) {
69          checkNotNull(userKey, "userKey cannot be null");
70  
71          return getUserSettings(userKey.getStringValue());
72      }
73  
74      @Override
75      public synchronized void updateUserSettings(String username, Function<UserSettingsBuilder, UserSettings> updateFunction) {
76          final UserSettings userSettings = updateFunction.apply(UserSettingsBuilderRefAppImpl.builder(getUserSettings(username)));
77  
78          // check for keys being too long, or values being too long
79          for (String key : userSettings.getKeys()) {
80              if (key.length() > UserSettingsService.MAX_KEY_LENGTH) {
81                  throw new IllegalArgumentException("Key: \"" + key + "\" too long");
82              }
83              final Option<String> option = userSettings.getString(key);
84              for (String value : option) {
85                  if (value.length() > UserSettingsService.MAX_STRING_VALUE_LENGTH) {
86                      throw new IllegalArgumentException("Value: \"" + value + "\" too long");
87                  }
88              }
89          }
90  
91          // This is not performant, but no product should be basing their impl on the Refapp's
92          SettingsMap settingsMap = new SettingsMap(username);
93          settingsMap.clear();
94  
95          for (String key : userSettings.getKeys()) {
96              // can short circuit in each for loop, as options are either of size 0 or 1 (and we know that if one of the
97              // options is non-empty, all others will be empty
98              for (Boolean value : userSettings.getBoolean(key)) {
99                  settingsMap.put(key, "B" + value);
100             }
101             for (Long value : userSettings.getLong(key)) {
102                 settingsMap.put(key, "L" + value);
103             }
104             for (String value : userSettings.getString(key)) {
105                 settingsMap.put(key, "S" + value);
106             }
107         }
108     }
109 
110     @Override
111     public void updateUserSettings(UserKey userKey, Function<UserSettingsBuilder, UserSettings> updateFunction) {
112         checkNotNull(userKey, "userKey cannot be null");
113         updateUserSettings(userKey.getStringValue(), updateFunction);
114     }
115 
116     // workaround for SAL-343
117     private static class UserSettingsBuilderRefAppImpl implements UserSettingsBuilder {
118         static UserSettingsBuilderRefAppImpl builder() {
119             return new UserSettingsBuilderRefAppImpl(DefaultUserSettings.builder());
120         }
121 
122         static UserSettingsBuilderRefAppImpl builder(UserSettings settings) {
123             return new UserSettingsBuilderRefAppImpl(DefaultUserSettings.builder(settings));
124         }
125 
126         private final UserSettingsBuilder original;
127 
128         private UserSettingsBuilderRefAppImpl(UserSettingsBuilder original) {
129             this.original = original;
130         }
131 
132         @Override
133         public UserSettingsBuilder put(String key, String value) {
134             return original.put(key, value);
135         }
136 
137         @Override
138         public UserSettingsBuilder put(String key, boolean value) {
139             return original.put(key, value);
140         }
141 
142         @Override
143         public UserSettingsBuilder put(String key, long value) {
144             return original.put(key, value);
145         }
146 
147         @Override
148         public UserSettingsBuilder remove(String key) {
149             return original.remove(key);
150         }
151 
152         @Override
153         public Option<Object> get(String key) {
154             return original.get(key);
155         }
156 
157         @Override
158         public Set<String> getKeys() {
159             // make defensive copy to allow for iterating over the keys by clients
160             return ImmutableSet.copyOf(original.getKeys());
161         }
162 
163         @Override
164         public UserSettings build() {
165             // double copy to work around SAL-343 - this will prevent manipulating the settings by manipulating this
166             // builder
167             return DefaultUserSettings.builder(original.build()).build();
168         }
169     }
170 }