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