/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.user.server;
import com.google.common.util.concurrent.Striped;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.user.server.spi.PreferenceDao;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import static java.util.Objects.requireNonNull;
/**
* Preferences manager layer, simplifies preferences service by
* taking all the business logic out from the service and making that logic easily
* reusable throughout the system.
*
* <p>The manager doesn't perform any bean validations and it
* is expected that all the incoming objects are valid, nevertheless
* this exactly the right place for performing business validations.
*
* @author Yevhenii Voevodin
*/
@Singleton
public class PreferenceManager {
private static final Striped<Lock> UPDATE_REENTRANT_LOCKS = Striped.lazyWeakLock(32);
@Inject
private PreferenceDao preferenceDao;
/**
* Associates the given {@code preferences} with the given {@code userId}.
*
* <p>Note that this method will override all the existing properties
* for the user with id {@code userId}.
*
* @param userId
* the user id whom the {@code preferences} belong to
* @param preferences
* the preferences to associate with the {@code userId}
* @throws NullPointerException
* when either {@code userId} or {@code preferences} is null
* @throws ServerException
* when any error occurs
*/
public void save(String userId, Map<String, String> preferences) throws ServerException {
requireNonNull(userId, "Required non-null user id");
requireNonNull(preferences, "Required non-null preferences");
preferenceDao.setPreferences(userId, preferences);
}
/**
* Updates the preferences of the user by merging given {@code preferences}
* with the existing preferences. If user doesn't have any preferences
* then the given {@code preferences} will be associated with the user.
*
* @param userId
* the user whose preferences should be updated
* @param preferences
* preferences update
* @return all the user's preferences including the update
* @throws NullPointerException
* when either {@code userId} or {@code preferences} is null
* @throws ServerException
* when any error occurs
*/
public Map<String, String> update(String userId, Map<String, String> preferences) throws ServerException {
requireNonNull(userId, "Required non-null user id");
requireNonNull(preferences, "Required non-null preferences");
// Holding reference to prevent garbage collection
// this reentrantLock helps to avoid race-conditions when parallel updates are applied
final Lock reentrantLock = UPDATE_REENTRANT_LOCKS.get(userId);
reentrantLock.lock();
try {
final Map<String, String> found = preferenceDao.getPreferences(userId);
found.putAll(preferences);
preferenceDao.setPreferences(userId, found);
return found;
} finally {
reentrantLock.unlock();
}
}
/**
* Finds user's preferences.
*
* @param userId
* user id to find preferences
* @return found preferences or empty map, if there are no preferences related to user
* @throws NullPointerException
* when {@code userId} is null
* @throws ServerException
* when any error occurs
*/
public Map<String, String> find(String userId) throws ServerException {
requireNonNull(userId, "Required non-null user id");
return preferenceDao.getPreferences(userId);
}
/**
* Finds user's preferences.
*
* @param userId
* user id to find preferences
* @param keyFilter
* regex which is used to filter preferences by keys, so
* result contains only the user's preferences that match {@code keyFilter} regex
* @return found preferences filtered by {@code keyFilter} or an empty map
* if there are no preferences related to user
* @throws NullPointerException
* when {@code userId} is null
* @throws ServerException
* when any error occurs
*/
public Map<String, String> find(String userId, String keyFilter) throws ServerException {
requireNonNull(userId, "Required non-null user id");
return preferenceDao.getPreferences(userId, keyFilter);
}
/**
* Removes(clears) user's preferences.
*
* @param userId
* the id of the user to remove preferences
* @throws NullPointerException
* when {@code userId} is null
* @throws ServerException
* when any error occurs
*/
public void remove(String userId) throws ServerException {
requireNonNull(userId, "Required non-null user id");
preferenceDao.remove(userId);
}
/**
* Removes the preferences with the given {@code names}.
*
* @param userId
* the id of the user to remove preferences
* @param names
* the names to remove
* @throws NullPointerException
* when either {@code userId} or {@code names} is null
* @throws ServerException
* when any error occurs
*/
public void remove(String userId, List<String> names) throws ServerException {
requireNonNull(userId, "Required non-null user id");
requireNonNull(names, "Required non-null preference names");
// Holding reference to prevent garbage collection
// this reentrantLock helps to avoid race-conditions when parallel updates are applied
final Lock reentrantLock = UPDATE_REENTRANT_LOCKS.get(userId);
reentrantLock.lock();
try {
final Map<String, String> preferences = preferenceDao.getPreferences(userId);
names.forEach(preferences::remove);
preferenceDao.setPreferences(userId, preferences);
} finally {
reentrantLock.unlock();
}
}
}