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 all user's preference values (as String) for a given context. 083 * @param storageContext the preferences context. 084 * @param contextVars the context variables. 085 * @return the user preference values as a Map of String indexed by preference ID. 086 * @throws UserPreferencesException if an error occurs getting the preferences. 087 */ 088 public Map<UserIdentity, Map<String, String>> getAllUnTypedUserPrefs(String storageContext, Map<String, String> contextVars) throws UserPreferencesException 089 { 090 Map<UserIdentity, Map<String, String>> preferences = new HashMap<>(); 091 092 Set<String> storageRoles = getStorageRoles(contextVars); 093 094 for (String storageRole : storageRoles) 095 { 096 UserPreferencesStorage storageManager = getStorageManager(storageRole); 097 preferences.putAll(storageManager.getAllUnTypedUserPrefs(storageContext, contextVars)); 098 } 099 100 return preferences; 101 } 102 103 /** 104 * Get a user's preference values (as String) for a given context. 105 * @param user the user. 106 * @param storageContext the preferences context. 107 * @param contextVars the context variables. 108 * @return the user preference values as a Map of String indexed by preference ID. 109 * @throws UserPreferencesException if an error occurs getting the preferences. 110 */ 111 public Map<String, String> getUnTypedUserPrefs(UserIdentity user, String storageContext, Map<String, String> contextVars) throws UserPreferencesException 112 { 113 Map<String, String> preferences = new HashMap<>(); 114 115// Map<String, Collection<UserPreference>> userPrefsByStorage = getUserPrefsByStorage(contextVars); 116 Set<String> storageRoles = getStorageRoles(contextVars); 117 118 for (String storageRole : storageRoles) 119 { 120 UserPreferencesStorage storageManager = getStorageManager(storageRole); 121 122 preferences.putAll(storageManager.getUnTypedUserPrefs(user, storageContext, contextVars)); 123 } 124 125 return preferences; 126 } 127 128 129 /** 130 * Get a user's preference values cast as their own type for a given context. 131 * @param user the user. 132 * @param storageContext the preferences context. 133 * @param contextVars the context variables. 134 * @return the user preference values as a Map of Object indexed by preference ID. 135 * @throws UserPreferencesException if an error occurs getting the preferences. 136 */ 137 public Map<String, Object> getTypedUserPrefs(UserIdentity user, String storageContext, Map<String, String> contextVars) throws UserPreferencesException 138 { 139 Map<String, String> unTypedUserPrefs = getUnTypedUserPrefs(user, storageContext, contextVars); 140 141 return _castValues(unTypedUserPrefs, contextVars); 142 } 143 144 /** 145 * Add a user preference 146 * @param user the user. 147 * @param storageContext the preferences context. 148 * @param contextVars the context variables. 149 * @param name the user pref name 150 * @param value the user pref value 151 * @throws UserPreferencesException if an error occurred 152 */ 153 public void addUserPreference(UserIdentity user, String storageContext, Map<String, String> contextVars, String name, String value) throws UserPreferencesException 154 { 155 Map<String, String> userPrefs = getUnTypedUserPrefs(user, storageContext, contextVars); 156 userPrefs.put(name, value); 157 158 setUserPreferences(user, storageContext, contextVars, userPrefs); 159 } 160 161 /** 162 * Add a user preference 163 * @param user the user. 164 * @param storageContext the preferences context. 165 * @param contextVars the context variables. 166 * @param values the user prefs to add 167 * @throws UserPreferencesException if an error occurred 168 */ 169 public void addUserPreferences(UserIdentity user, String storageContext, Map<String, String> contextVars, Map<String, String> values) throws UserPreferencesException 170 { 171 Map<String, String> userPrefs = getUnTypedUserPrefs(user, storageContext, contextVars); 172 userPrefs.putAll(values); 173 174 setUserPreferences(user, storageContext, contextVars, userPrefs); 175 } 176 177 /** 178 * Remove a user preference 179 * @param user the user. 180 * @param storageContext the preferences context. 181 * @param contextVars the context variables. 182 * @param name the user pref name 183 * @throws UserPreferencesException if an error occurred 184 */ 185 public void removeUserPreference(UserIdentity user, String storageContext, Map<String, String> contextVars, String name) throws UserPreferencesException 186 { 187 Map<String, String> userPrefs = getUnTypedUserPrefs(user, storageContext, contextVars); 188 if (userPrefs.containsKey(name)) 189 { 190 userPrefs.remove(name); 191 } 192 setUserPreferences(user, storageContext, contextVars, userPrefs); 193 } 194 195 /** 196 * Remove all user preferences. 197 * @param user the user. 198 * @param storageContext the preferences context. 199 * @param contextVars the context variables. 200 * @throws UserPreferencesException if an error occurred 201 */ 202 public void removeAllUserPreferences(UserIdentity user, String storageContext, Map<String, String> contextVars) throws UserPreferencesException 203 { 204 Set<String> storageRoles = getStorageRoles(contextVars); 205 206 for (String storageRole : storageRoles) 207 { 208 UserPreferencesStorage storageManager = getStorageManager(storageRole); 209 210 storageManager.removeUserPreferences(user, storageContext, contextVars); 211 } 212 } 213 214 /** 215 * Set a user's preferences for a given context. 216 * @param user the user. 217 * @param storageContext the preferences context. 218 * @param contextVars the context variables. 219 * @param preferenceValues a Map of the preference values indexed by ID. 220 * @throws UserPreferencesException if an error occurred 221 */ 222 public void setUserPreferences(UserIdentity user, String storageContext, Map<String, String> contextVars, Map<String, String> preferenceValues) throws UserPreferencesException 223 { 224 Map<String, Map<String, String>> preferenceValuesByStorage = getUserPrefsValuesByStorage(contextVars, preferenceValues); 225 226 for (String managerRole : preferenceValuesByStorage.keySet()) 227 { 228 Map<String, String> storagePrefValues = preferenceValuesByStorage.get(managerRole); 229 230 // Retrieve the right storage manager. 231 UserPreferencesStorage storageManager = getStorageManager(managerRole); 232 233 // Call set on the storage manager. 234 storageManager.setUserPreferences(user, storageContext, contextVars, storagePrefValues); 235 } 236 } 237 238 /** 239 * Get a single string user preference value for a given context. 240 * @param user the user. 241 * @param storageContext the preferences context. 242 * @param contextVars the context variables. 243 * @param id the preference ID. 244 * @return the user preference value as a String. 245 * @throws UserPreferencesException if an error occurred 246 */ 247 public String getUserPreferenceAsString(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 248 { 249 UserPreferencesStorage storageManager = getStorageManager(contextVars, id); 250 251 return storageManager.getUserPreferenceAsString(user, storageContext, contextVars, id); 252 } 253 254 /** 255 * Get a single long user preference value for a given context. 256 * @param user the user. 257 * @param storageContext the preferences context. 258 * @param contextVars the context variables. 259 * @param id the preference ID. 260 * @return the user preference value as a Long. 261 * @throws UserPreferencesException if an error occurred 262 */ 263 public Long getUserPreferenceAsLong(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 264 { 265 UserPreferencesStorage storageManager = getStorageManager(contextVars, id); 266 267 return storageManager.getUserPreferenceAsLong(user, storageContext, contextVars, id); 268 } 269 270 /** 271 * Get a single date user preference value for a given context. 272 * @param user the user. 273 * @param storageContext the preferences context. 274 * @param contextVars the context variables. 275 * @param id the preference ID. 276 * @return the user preference value as a Date. 277 * @throws UserPreferencesException if an error occurred 278 */ 279 public ZonedDateTime getUserPreferenceAsDate(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 280 { 281 UserPreferencesStorage storageManager = getStorageManager(contextVars, id); 282 283 return storageManager.getUserPreferenceAsDate(user, storageContext, contextVars, id); 284 } 285 286 /** 287 * Get a single boolean user preference value for a given context. 288 * @param user the user. 289 * @param storageContext the preferences context. 290 * @param contextVars the context variables. 291 * @param id the preference ID. 292 * @return the user preference value as a Boolean. 293 * @throws UserPreferencesException if an error occurred 294 */ 295 public Boolean getUserPreferenceAsBoolean(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 296 { 297 UserPreferencesStorage storageManager = getStorageManager(contextVars, id); 298 299 return storageManager.getUserPreferenceAsBoolean(user, storageContext, contextVars, id); 300 } 301 302 /** 303 * Get a single double user preference value for a given context. 304 * @param user the user. 305 * @param storageContext the preferences context. 306 * @param contextVars the context variables. 307 * @param id the preference ID. 308 * @return the user preference value as a Double. 309 * @throws UserPreferencesException if an error occurred 310 */ 311 public Double getUserPreferenceAsDouble(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 312 { 313 UserPreferencesStorage storageManager = getStorageManager(contextVars, id); 314 315 return storageManager.getUserPreferenceAsDouble(user, storageContext, contextVars, id); 316 } 317 318 /** 319 * Get all user preference storages. 320 * @param contextVars the context variables. 321 * @return a Set of storage roles. 322 */ 323 protected Set<String> getStorageRoles(Map<String, String> contextVars) 324 { 325 Set<String> storageRoles = new HashSet<>(); 326 327 // Add the default storage role. 328 storageRoles.add(_defaultStorageRole); 329 330 for (UserPreference preference : _userPrefEP.getUserPreferences(contextVars)) 331 { 332 String role = StringUtils.defaultIfEmpty(preference.getManagerRole(), _defaultStorageRole); 333 334 storageRoles.add(role); 335 } 336 337 return storageRoles; 338 } 339 340 /** 341 * Get all user preferences, grouped by storage point. 342 * @param contextVars the context variables. 343 * @return a Map of storage role -> collection of user preferences. 344 */ 345 protected Map<String, Collection<UserPreference>> getUserPrefsByStorage(Map<String, String> contextVars) 346 { 347 Map<String, Collection<UserPreference>> userPrefs = new HashMap<>(); 348 349 for (UserPreference preference : _userPrefEP.getUserPreferences(contextVars)) 350 { 351 String role = StringUtils.defaultIfEmpty(preference.getManagerRole(), _defaultStorageRole); 352 353 Collection<UserPreference> rolePrefs = userPrefs.get(role); 354 if (rolePrefs == null) 355 { 356 rolePrefs = new ArrayList<>(); 357 userPrefs.put(role, rolePrefs); 358 } 359 360 rolePrefs.add(preference); 361 } 362 363 return userPrefs; 364 } 365 366 /** 367 * Get user preference values, divided up by storage role. 368 * @param contextVars the context variables. 369 * @param preferenceValues the unsorted preference values. 370 * @return the preference values, divided up by storage role, as a Map of storage role -gt; preference values. 371 */ 372 protected Map<String, Map<String, String>> getUserPrefsValuesByStorage(Map<String, String> contextVars, Map<String, String> preferenceValues) 373 { 374 Map<String, Map<String, String>> preferenceValuesByStorage = new HashMap<>(); 375 376 // Initialize with empty maps. 377 for (String storageRole : getStorageRoles(contextVars)) 378 { 379 preferenceValuesByStorage.put(storageRole, new HashMap<>()); 380 } 381 382 Map<String, Collection<UserPreference>> userPrefsByStorage = getUserPrefsByStorage(contextVars); 383 Map<String, String> unknownPreferenceValues = new HashMap<>(preferenceValues); 384 385 for (String storageRole : userPrefsByStorage.keySet()) 386 { 387 Map<String, String> storagePrefValues = preferenceValuesByStorage.get(storageRole); 388 389 // Iterate over declared user preferences for this storage. 390 Collection<UserPreference> storageUserPrefs = userPrefsByStorage.get(storageRole); 391 for (UserPreference pref : storageUserPrefs) 392 { 393 String prefId = pref.getName(); 394 if (preferenceValues.containsKey(prefId)) 395 { 396 // Add the value to the corresponding storage map. 397 storagePrefValues.put(prefId, preferenceValues.get(prefId)); 398 // Remove the value from the unknown preferences. 399 unknownPreferenceValues.remove(prefId); 400 } 401 } 402 } 403 404 Map<String, String> defaultStorageRoleValues = preferenceValuesByStorage.get(_defaultStorageRole); 405 if (!unknownPreferenceValues.isEmpty()) 406 { 407 // At this point, the unknownPreferenceValues map contains only undeclared preferences: 408 // add them to the default storage. 409 defaultStorageRoleValues.putAll(unknownPreferenceValues); 410 } 411 // If no value is to be set in the default storage role, remove the role from the preference values by storage 412 else if (defaultStorageRoleValues.isEmpty()) 413 { 414 preferenceValuesByStorage.remove(_defaultStorageRole); 415 } 416 417 return preferenceValuesByStorage; 418 } 419 420 /** 421 * Get the storage component for a given role. 422 * @param role the storage component role. 423 * @return the storage component. 424 * @throws UserPreferencesException if an error occurs looking up the storage manager. 425 */ 426 protected UserPreferencesStorage getStorageManager(String role) throws UserPreferencesException 427 { 428 UserPreferencesStorage storageManager = null; 429 430 String componentRole = role; 431 432 if (_storageManagers.containsKey(componentRole)) 433 { 434 storageManager = _storageManagers.get(componentRole); 435 } 436 else 437 { 438 try 439 { 440 storageManager = (UserPreferencesStorage) _serviceManager.lookup(componentRole); 441 _storageManagers.put(componentRole, storageManager); 442 } 443 catch (ServiceException e) 444 { 445 throw new UserPreferencesException("Error looking up the user preference storage component of role " + componentRole, e); 446 } 447 } 448 449 return storageManager; 450 } 451 452 /** 453 * Get the storage component for a given role. 454 * @param contextVars The context vars 455 * @param id The preference id 456 * @return the storage component. 457 * @throws UserPreferencesException if an error occurs looking up the storage manager. 458 */ 459 protected UserPreferencesStorage getStorageManager(Map<String, String> contextVars, String id) throws UserPreferencesException 460 { 461 String storageManagerRole = _defaultStorageRole; 462 463 UserPreference preference = _userPrefEP.getUserPreference(contextVars, id); 464 465 if (preference != null && StringUtils.isNotEmpty(preference.getManagerRole())) 466 { 467 storageManagerRole = preference.getManagerRole(); 468 } 469 470 return getStorageManager(storageManagerRole); 471 } 472 473 /** 474 * Cast the preference values as their real type. 475 * @param untypedValues the untyped user preferences 476 * @param contextVars the context variables. 477 * @return typed user preferences 478 */ 479 protected Map<String, Object> _castValues(Map<String, String> untypedValues, Map<String, String> contextVars) 480 { 481 Map<String, Object> typedValues = new HashMap<>(untypedValues.size()); 482 483 for (Entry<String, String> entry : untypedValues.entrySet()) 484 { 485 UserPreference userPref = _userPrefEP.getUserPreference(contextVars, entry.getKey()); 486 if (userPref != null) 487 { 488 Object value = userPref.getType().castValue(entry.getValue()); 489 typedValues.put(userPref.getName(), value); 490 } 491 else 492 { 493 typedValues.put(entry.getKey(), entry.getValue()); 494 } 495 } 496 497 return typedValues; 498 } 499 500}