001/* 002 * Copyright 2012 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.core.userpref; 017 018import java.time.ZonedDateTime; 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.Map; 024import java.util.Map.Entry; 025import java.util.Set; 026 027import org.apache.avalon.framework.activity.Initializable; 028import org.apache.avalon.framework.component.Component; 029import org.apache.avalon.framework.configuration.Configurable; 030import org.apache.avalon.framework.configuration.Configuration; 031import org.apache.avalon.framework.configuration.ConfigurationException; 032import org.apache.avalon.framework.logger.AbstractLogEnabled; 033import org.apache.avalon.framework.service.ServiceException; 034import org.apache.avalon.framework.service.ServiceManager; 035import org.apache.avalon.framework.service.Serviceable; 036import org.apache.avalon.framework.thread.ThreadSafe; 037import org.apache.commons.lang3.StringUtils; 038 039import org.ametys.core.user.UserIdentity; 040 041/** 042 * Component handling user preference values retrieval and storage. 043 */ 044public class UserPreferencesManager extends AbstractLogEnabled implements ThreadSafe, Component, Serviceable, Configurable, Initializable 045{ 046 047 /** The avalon role. */ 048 public static final String ROLE = UserPreferencesManager.class.getName(); 049 050 /** The user preferences extensions point. */ 051 protected UserPreferencesExtensionPoint _userPrefEP; 052 053 /** A list of storage managers. */ 054 protected Map<String, UserPreferencesStorage> _storageManagers; 055 056 /** The default storage component role. */ 057 protected String _defaultStorageRole; 058 059 /** The avalon service manager. */ 060 protected ServiceManager _serviceManager; 061 062 @Override 063 public void service(ServiceManager manager) throws ServiceException 064 { 065 _serviceManager = manager; 066 _userPrefEP = (UserPreferencesExtensionPoint) manager.lookup(UserPreferencesExtensionPoint.ROLE); 067 } 068 069 @Override 070 public void configure(Configuration configuration) throws ConfigurationException 071 { 072 _defaultStorageRole = configuration.getChild("default-storage-role").getValue(); 073 } 074 075 @Override 076 public void initialize() throws Exception 077 { 078 _storageManagers = new HashMap<>(); 079 } 080 081 /** 082 * Get a user's preference values (as String) for a given context. 083 * @param user the user. 084 * @param storageContext the preferences context. 085 * @param contextVars the context variables. 086 * @return the user preference values as a Map of String indexed by preference ID. 087 * @throws UserPreferencesException if an error occurs getting the preferences. 088 */ 089 public Map<String, String> getUnTypedUserPrefs(UserIdentity user, String storageContext, Map<String, String> contextVars) throws UserPreferencesException 090 { 091 Map<String, String> preferences = new HashMap<>(); 092 093// Map<String, Collection<UserPreference>> userPrefsByStorage = getUserPrefsByStorage(contextVars); 094 Set<String> storageRoles = getStorageRoles(contextVars); 095 096 for (String storageRole : storageRoles) 097 { 098 UserPreferencesStorage storageManager = getStorageManager(storageRole); 099 100 preferences.putAll(storageManager.getUnTypedUserPrefs(user, storageContext, contextVars)); 101 } 102 103 return preferences; 104 } 105 106 107 /** 108 * Get a user's preference values cast as their own type for a given context. 109 * @param user the user. 110 * @param storageContext the preferences context. 111 * @param contextVars the context variables. 112 * @return the user preference values as a Map of Object indexed by preference ID. 113 * @throws UserPreferencesException if an error occurs getting the preferences. 114 */ 115 public Map<String, Object> getTypedUserPrefs(UserIdentity user, String storageContext, Map<String, String> contextVars) throws UserPreferencesException 116 { 117 Map<String, String> unTypedUserPrefs = getUnTypedUserPrefs(user, storageContext, contextVars); 118 119 return _castValues(unTypedUserPrefs, contextVars); 120 } 121 122 /** 123 * Add a user preference 124 * @param user the user. 125 * @param storageContext the preferences context. 126 * @param contextVars the context variables. 127 * @param name the user pref name 128 * @param value the user pref value 129 * @throws UserPreferencesException if an error occurred 130 */ 131 public void addUserPreference(UserIdentity user, String storageContext, Map<String, String> contextVars, String name, String value) throws UserPreferencesException 132 { 133 Map<String, String> userPrefs = getUnTypedUserPrefs(user, storageContext, contextVars); 134 userPrefs.put(name, value); 135 136 setUserPreferences(user, storageContext, contextVars, userPrefs); 137 } 138 139 /** 140 * Add a user preference 141 * @param user the user. 142 * @param storageContext the preferences context. 143 * @param contextVars the context variables. 144 * @param values the user prefs to add 145 * @throws UserPreferencesException if an error occurred 146 */ 147 public void addUserPreferences(UserIdentity user, String storageContext, Map<String, String> contextVars, Map<String, String> values) throws UserPreferencesException 148 { 149 Map<String, String> userPrefs = getUnTypedUserPrefs(user, storageContext, contextVars); 150 userPrefs.putAll(values); 151 152 setUserPreferences(user, storageContext, contextVars, userPrefs); 153 } 154 155 /** 156 * Remove a user preference 157 * @param user the user. 158 * @param storageContext the preferences context. 159 * @param contextVars the context variables. 160 * @param name the user pref name 161 * @throws UserPreferencesException if an error occurred 162 */ 163 public void removeUserPreference(UserIdentity user, String storageContext, Map<String, String> contextVars, String name) throws UserPreferencesException 164 { 165 Map<String, String> userPrefs = getUnTypedUserPrefs(user, storageContext, contextVars); 166 if (userPrefs.containsKey(name)) 167 { 168 userPrefs.remove(name); 169 } 170 setUserPreferences(user, storageContext, contextVars, userPrefs); 171 } 172 173 /** 174 * Remove all user preferences. 175 * @param user the user. 176 * @param storageContext the preferences context. 177 * @param contextVars the context variables. 178 * @throws UserPreferencesException if an error occurred 179 */ 180 public void removeAllUserPreferences(UserIdentity user, String storageContext, Map<String, String> contextVars) throws UserPreferencesException 181 { 182 Set<String> storageRoles = getStorageRoles(contextVars); 183 184 for (String storageRole : storageRoles) 185 { 186 UserPreferencesStorage storageManager = getStorageManager(storageRole); 187 188 storageManager.removeUserPreferences(user, storageContext, contextVars); 189 } 190 } 191 192 /** 193 * Set a user's preferences for a given context. 194 * @param user the user. 195 * @param storageContext the preferences context. 196 * @param contextVars the context variables. 197 * @param preferenceValues a Map of the preference values indexed by ID. 198 * @throws UserPreferencesException if an error occurred 199 */ 200 public void setUserPreferences(UserIdentity user, String storageContext, Map<String, String> contextVars, Map<String, String> preferenceValues) throws UserPreferencesException 201 { 202 Map<String, Map<String, String>> preferenceValuesByStorage = getUserPrefsValuesByStorage(contextVars, preferenceValues); 203 204 for (String managerRole : preferenceValuesByStorage.keySet()) 205 { 206 Map<String, String> storagePrefValues = preferenceValuesByStorage.get(managerRole); 207 208 // Retrieve the right storage manager. 209 UserPreferencesStorage storageManager = getStorageManager(managerRole); 210 211 // Call set on the storage manager. 212 storageManager.setUserPreferences(user, storageContext, contextVars, storagePrefValues); 213 } 214 } 215 216 /** 217 * Get a single string user preference value for a given context. 218 * @param user the user. 219 * @param storageContext the preferences context. 220 * @param contextVars the context variables. 221 * @param id the preference ID. 222 * @return the user preference value as a String. 223 * @throws UserPreferencesException if an error occurred 224 */ 225 public String getUserPreferenceAsString(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 226 { 227 UserPreferencesStorage storageManager = getStorageManager(contextVars, id); 228 229 return storageManager.getUserPreferenceAsString(user, storageContext, contextVars, id); 230 } 231 232 /** 233 * Get a single long user preference value for a given context. 234 * @param user the user. 235 * @param storageContext the preferences context. 236 * @param contextVars the context variables. 237 * @param id the preference ID. 238 * @return the user preference value as a Long. 239 * @throws UserPreferencesException if an error occurred 240 */ 241 public Long getUserPreferenceAsLong(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 242 { 243 UserPreferencesStorage storageManager = getStorageManager(contextVars, id); 244 245 return storageManager.getUserPreferenceAsLong(user, storageContext, contextVars, id); 246 } 247 248 /** 249 * Get a single date user preference value for a given context. 250 * @param user the user. 251 * @param storageContext the preferences context. 252 * @param contextVars the context variables. 253 * @param id the preference ID. 254 * @return the user preference value as a Date. 255 * @throws UserPreferencesException if an error occurred 256 */ 257 public ZonedDateTime getUserPreferenceAsDate(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 258 { 259 UserPreferencesStorage storageManager = getStorageManager(contextVars, id); 260 261 return storageManager.getUserPreferenceAsDate(user, storageContext, contextVars, id); 262 } 263 264 /** 265 * Get a single boolean user preference value for a given context. 266 * @param user the user. 267 * @param storageContext the preferences context. 268 * @param contextVars the context variables. 269 * @param id the preference ID. 270 * @return the user preference value as a Boolean. 271 * @throws UserPreferencesException if an error occurred 272 */ 273 public Boolean getUserPreferenceAsBoolean(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 274 { 275 UserPreferencesStorage storageManager = getStorageManager(contextVars, id); 276 277 return storageManager.getUserPreferenceAsBoolean(user, storageContext, contextVars, id); 278 } 279 280 /** 281 * Get a single double user preference value for a given context. 282 * @param user the user. 283 * @param storageContext the preferences context. 284 * @param contextVars the context variables. 285 * @param id the preference ID. 286 * @return the user preference value as a Double. 287 * @throws UserPreferencesException if an error occurred 288 */ 289 public Double getUserPreferenceAsDouble(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 290 { 291 UserPreferencesStorage storageManager = getStorageManager(contextVars, id); 292 293 return storageManager.getUserPreferenceAsDouble(user, storageContext, contextVars, id); 294 } 295 296 /** 297 * Get all user preference storages. 298 * @param contextVars the context variables. 299 * @return a Set of storage roles. 300 */ 301 protected Set<String> getStorageRoles(Map<String, String> contextVars) 302 { 303 Set<String> storageRoles = new HashSet<>(); 304 305 // Add the default storage role. 306 storageRoles.add(_defaultStorageRole); 307 308 for (UserPreference preference : _userPrefEP.getUserPreferences(contextVars)) 309 { 310 String role = StringUtils.defaultIfEmpty(preference.getManagerRole(), _defaultStorageRole); 311 312 storageRoles.add(role); 313 } 314 315 return storageRoles; 316 } 317 318 /** 319 * Get all user preferences, grouped by storage point. 320 * @param contextVars the context variables. 321 * @return a Map of storage role -> collection of user preferences. 322 */ 323 protected Map<String, Collection<UserPreference>> getUserPrefsByStorage(Map<String, String> contextVars) 324 { 325 Map<String, Collection<UserPreference>> userPrefs = new HashMap<>(); 326 327 for (UserPreference preference : _userPrefEP.getUserPreferences(contextVars)) 328 { 329 String role = StringUtils.defaultIfEmpty(preference.getManagerRole(), _defaultStorageRole); 330 331 Collection<UserPreference> rolePrefs = userPrefs.get(role); 332 if (rolePrefs == null) 333 { 334 rolePrefs = new ArrayList<>(); 335 userPrefs.put(role, rolePrefs); 336 } 337 338 rolePrefs.add(preference); 339 } 340 341 return userPrefs; 342 } 343 344 /** 345 * Get user preference values, divided up by storage role. 346 * @param contextVars the context variables. 347 * @param preferenceValues the unsorted preference values. 348 * @return the preference values, divided up by storage role, as a Map of storage role -gt; preference values. 349 */ 350 protected Map<String, Map<String, String>> getUserPrefsValuesByStorage(Map<String, String> contextVars, Map<String, String> preferenceValues) 351 { 352 Map<String, Map<String, String>> preferenceValuesByStorage = new HashMap<>(); 353 354 // Initialize with empty maps. 355 for (String storageRole : getStorageRoles(contextVars)) 356 { 357 preferenceValuesByStorage.put(storageRole, new HashMap<>()); 358 } 359 360 Map<String, Collection<UserPreference>> userPrefsByStorage = getUserPrefsByStorage(contextVars); 361 Map<String, String> unknownPreferenceValues = new HashMap<>(preferenceValues); 362 363 for (String storageRole : userPrefsByStorage.keySet()) 364 { 365 Map<String, String> storagePrefValues = preferenceValuesByStorage.get(storageRole); 366 367 // Iterate over declared user preferences for this storage. 368 Collection<UserPreference> storageUserPrefs = userPrefsByStorage.get(storageRole); 369 for (UserPreference pref : storageUserPrefs) 370 { 371 String prefId = pref.getName(); 372 if (preferenceValues.containsKey(prefId)) 373 { 374 // Add the value to the corresponding storage map. 375 storagePrefValues.put(prefId, preferenceValues.get(prefId)); 376 // Remove the value from the unknown preferences. 377 unknownPreferenceValues.remove(prefId); 378 } 379 } 380 } 381 382 Map<String, String> defaultStorageRoleValues = preferenceValuesByStorage.get(_defaultStorageRole); 383 if (!unknownPreferenceValues.isEmpty()) 384 { 385 // At this point, the unknownPreferenceValues map contains only undeclared preferences: 386 // add them to the default storage. 387 defaultStorageRoleValues.putAll(unknownPreferenceValues); 388 } 389 // If no value is to be set in the default storage role, remove the role from the preference values by storage 390 else if (defaultStorageRoleValues.isEmpty()) 391 { 392 preferenceValuesByStorage.remove(_defaultStorageRole); 393 } 394 395 return preferenceValuesByStorage; 396 } 397 398 /** 399 * Get the storage component for a given role. 400 * @param role the storage component role. 401 * @return the storage component. 402 * @throws UserPreferencesException if an error occurs looking up the storage manager. 403 */ 404 protected UserPreferencesStorage getStorageManager(String role) throws UserPreferencesException 405 { 406 UserPreferencesStorage storageManager = null; 407 408 String componentRole = role; 409 410 if (_storageManagers.containsKey(componentRole)) 411 { 412 storageManager = _storageManagers.get(componentRole); 413 } 414 else 415 { 416 try 417 { 418 storageManager = (UserPreferencesStorage) _serviceManager.lookup(componentRole); 419 _storageManagers.put(componentRole, storageManager); 420 } 421 catch (ServiceException e) 422 { 423 throw new UserPreferencesException("Error looking up the user preference storage component of role " + componentRole, e); 424 } 425 } 426 427 return storageManager; 428 } 429 430 /** 431 * Get the storage component for a given role. 432 * @param contextVars The context vars 433 * @param id The preference id 434 * @return the storage component. 435 * @throws UserPreferencesException if an error occurs looking up the storage manager. 436 */ 437 protected UserPreferencesStorage getStorageManager(Map<String, String> contextVars, String id) throws UserPreferencesException 438 { 439 String storageManagerRole = _defaultStorageRole; 440 441 UserPreference preference = _userPrefEP.getUserPreference(contextVars, id); 442 443 if (preference != null && StringUtils.isNotEmpty(preference.getManagerRole())) 444 { 445 storageManagerRole = preference.getManagerRole(); 446 } 447 448 return getStorageManager(storageManagerRole); 449 } 450 451 /** 452 * Cast the preference values as their real type. 453 * @param untypedValues the untyped user preferences 454 * @param contextVars the context variables. 455 * @return typed user preferences 456 */ 457 protected Map<String, Object> _castValues(Map<String, String> untypedValues, Map<String, String> contextVars) 458 { 459 Map<String, Object> typedValues = new HashMap<>(untypedValues.size()); 460 461 for (Entry<String, String> entry : untypedValues.entrySet()) 462 { 463 UserPreference userPref = _userPrefEP.getUserPreference(contextVars, entry.getKey()); 464 if (userPref != null) 465 { 466 Object value = userPref.getType().castValue(entry.getValue()); 467 typedValues.put(userPref.getName(), value); 468 } 469 else 470 { 471 typedValues.put(entry.getKey(), entry.getValue()); 472 } 473 } 474 475 return typedValues; 476 } 477 478}