Initial implementation to register PreferenceConverters on the UserStore

and set/get preference objects that are being held in memory for fast
access
This commit is contained in:
Steffen Schaefer committed 2016-07-04 17:07:01 +02:00
1 parent b404b42196
commit d77f6adb43
2 files changed
+141 -2

No files matched your search

@@ -7,10 +7,12 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.shiro.SecurityUtils;
import com.sap.sse.security.PreferenceConverter;
import com.sap.sse.security.SocialSettingsKeys;
import com.sap.sse.security.User;
import com.sap.sse.security.UserStore;
@@ -48,6 +50,18 @@ public class UserStoreImpl implements UserStore {
*/
private final ConcurrentHashMap<String, Map<String, String>> preferences;
/**
* Converter objects to map preference Strings to Objects.
* The keys must match the keys of the preferences.
*/
private final ConcurrentHashMap<String, PreferenceConverter<?>> preferenceConverters;
/**
* This is another view of the String preferences mapped by {@link #preferenceConverters} to Objects.
* Keys are the usernames, values are the key/value pairs representing the user's preferences.
*/
private final ConcurrentHashMap<String, Map<String, Object>> preferenceObjects;
/**
* Won't be serialized and remains <code>null</code> on the de-serializing end.
*/
@@ -65,6 +79,8 @@ public class UserStoreImpl implements UserStore {
settingTypes = new ConcurrentHashMap<>();
usersByAccessToken = new ConcurrentHashMap<>();
preferences = new ConcurrentHashMap<>();
preferenceConverters = new ConcurrentHashMap<>();
preferenceObjects = new ConcurrentHashMap<>();
this.mongoObjectFactory = mongoObjectFactory;
if (domainObjectFactory != null) {
for (Entry<String, Class<?>> e : domainObjectFactory.loadSettingTypes().entrySet()) {
@@ -105,6 +121,8 @@ public class UserStoreImpl implements UserStore {
@Override
public void clear() {
preferences.clear();
preferenceConverters.clear();
preferenceObjects.clear();
emailForUsername.clear();
settings.clear();
settingTypes.clear();
@@ -405,6 +423,11 @@ public class UserStoreImpl implements UserStore {
@Override
public void setPreference(String username, String key, String value) {
setPreferenceInternal(username, key, value);
loadPreferenceIfConverterIsAvailable(username, key);
}
private void setPreferenceInternal(String username, String key, String value) {
Map<String, String> userMap = preferences.get(username);
if (userMap == null) {
synchronized (preferences) {
@@ -431,6 +454,7 @@ public class UserStoreImpl implements UserStore {
mongoObjectFactory.storePreferences(username, userMap);
}
}
unsetPreferenceObject(username, key);
}
@Override
@@ -466,4 +490,97 @@ public class UserStoreImpl implements UserStore {
public Map<String, Class<?>> getAllSettingTypes() {
return settingTypes;
}
@Override
public void registerPreferenceConverter(String preferenceKey, PreferenceConverter<?> converter) {
PreferenceConverter<?> alreadyAssociatedConverter = preferenceConverters.putIfAbsent(preferenceKey, converter);
if(alreadyAssociatedConverter == null) {
final Set<String> usersToProcess = new HashSet<>(preferences.keySet());
for (String user : usersToProcess) {
loadPreferenceWithConverter(user, preferenceKey, converter);
}
} else {
logger.log(Level.SEVERE, "PreferenceConverter " + alreadyAssociatedConverter + " for key " + preferenceKey
+ " is already registered. Converter " + converter + " will not be registered");
}
}
private void loadPreferenceIfConverterIsAvailable(String username, String key) {
PreferenceConverter<?> preferenceConverter = preferenceConverters.get(key);
if (preferenceConverter != null) {
loadPreferenceWithConverter(username, key, preferenceConverter);
}
}
private void loadPreferenceWithConverter(String username, String key, PreferenceConverter<?> preferenceConverter) {
final String preferenceString = getPreference(username, key);
if (preferenceString != null) {
try {
final Object convertedObject = preferenceConverter.toPreference(preferenceString);
setPreferenceObjectInternal(username, key, convertedObject);
} catch (Throwable t) {
logger.log(Level.SEVERE, "Error while converting preference for key " + key + " from String \""
+ preferenceString + "\"", t);
}
}
}
private void setPreferenceObjectInternal(String username, String key, final Object convertedObject) {
Map<String, Object> userMap = preferenceObjects.get(username);
if (userMap == null) {
synchronized (preferenceObjects) {
// only synchronize when necessary
userMap = preferenceObjects.get(username);
if (userMap == null) {
userMap = new ConcurrentHashMap<>();
preferenceObjects.put(username, userMap);
}
}
}
userMap.put(key, convertedObject);
}
private void unsetPreferenceObject(String username, String key) {
Map<String, Object> userObjectMap = preferenceObjects.get(username);
if (userObjectMap != null) {
userObjectMap.remove(key);
}
}
@Override
public <T> T getPreferenceObject(String username, String key) {
final Object result;
Map<String, Object> userMap = preferenceObjects.get(username);
if (userMap != null) {
result = userMap.get(key);
} else {
result = null;
}
@SuppressWarnings("unchecked")
T resultT = (T) result;
return resultT;
}
@Override
public <T> void setPreferenceObject(String username, String key, Object preferenceObject)
throws IllegalArgumentException {
@SuppressWarnings("unchecked")
PreferenceConverter<Object> preferenceConverter = (PreferenceConverter<Object>) preferenceConverters.get(key);
if (preferenceConverter == null) {
throw new IllegalArgumentException("Setting preference for key "+key+" but there is no converter associated!");
}
if(preferenceObject == null) {
unsetPreference(username, key);
} else {
try {
String stringPreference = preferenceConverter.toString(preferenceObject);
setPreferenceInternal(username, key, stringPreference);
setPreferenceObjectInternal(username, key, preferenceObject);
} catch (Throwable t) {
logger.log(Level.SEVERE, "Error while converting preference for key " + key + " from Object \""
+ preferenceObject + "\"", t);
}
}
}
}
@@ -53,6 +53,30 @@ public interface UserStore extends Named {
void unsetPreference(String username, String key);
String getPreference(String username, String key);
/**
* Registers a converter objects for a preference key that is used to convert preference Strings to Objects. This
* makes it possible to access deserialized settings without the need to do the deserialization over and over again.
*
* @param key the key to associate the converter with
* @param converter the converter to use for (de)serialization
*/
void registerPreferenceConverter(String key, PreferenceConverter<?> converter);
/**
* Gets a preference object. Always returns null if there is no converter associated with the given key -> see
* {@link #registerPreferenceConverter(String, PreferenceConverter)}.
*/
<T> T getPreferenceObject(String username, String key);
/**
* Sets a preference as Object. This converts the given Object to a preference String using a
* {@link PreferenceConverter} that was registered through
* {@link #registerPreferenceConverter(String, PreferenceConverter)}.
*
* @throws IllegalArgumentException if there is no {@link PreferenceConverter} registered with the given key.
*/
<T> void setPreferenceObject(String username, String key, Object preferenceObject) throws IllegalArgumentException;
/**
* Sets a value for a key if that key was previously added to this store using {@link #addSetting(String, Class)}.
@@ -106,6 +130,4 @@ public interface UserStore extends Named {
* {@code username} no access token has previously been {@link #setAccessToken(String, String) set}.
*/
String getAccessToken(String username);
// void registerPreferenceConverter(String preferenceKey, PreferenceConverter<?> converter);
}