/*
* The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
* (the "License"). You may not use this work except in compliance with the License, which is
* available at www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied, as more fully set forth in the License.
*
* See the NOTICE file distributed with this work for information regarding copyright ownership.
*/
package alluxio.security.group;
import alluxio.Configuration;
import alluxio.PropertyKey;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
/**
* Class to map user to groups. It maintains a cache for user to groups mapping. The underlying
* implementation depends on {@link GroupMappingService}.
*/
public class CachedGroupMapping implements GroupMappingService {
/** The underlying implementation of GroupMappingService. */
private final GroupMappingService mService;
/** Whether the cache is enabled. Set timeout to non-positive value to disable cache. */
private final boolean mCacheEnabled;
/** A loading cache for user to groups mapping, refresh periodically. */
private LoadingCache<String, List<String>> mCache;
/** Max size of the cache. */
private static final long MAXSIZE = 10000;
/** Create an executor service that provide ListenableFuture instances. */
private final ThreadFactory mThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("UserGroupMappingCachePool-%d").setDaemon(true).build();
private final ExecutorService mParentExecutor = Executors.newSingleThreadExecutor(mThreadFactory);
private final ListeningExecutorService mExecutorService =
MoreExecutors.listeningDecorator(mParentExecutor);
/**
* Constructor with specified {@link GroupMappingService}. Initializes the cache if enabled.
*
* @param service group mapping service
*/
public CachedGroupMapping(GroupMappingService service) {
mService = service;
long timeoutMs = Long.parseLong(Configuration.get(
PropertyKey.SECURITY_GROUP_MAPPING_CACHE_TIMEOUT_MS));
mCacheEnabled = timeoutMs > 0;
if (mCacheEnabled) {
mCache = CacheBuilder.newBuilder()
// the maximum number of entries the cache may contain.
.maximumSize(MAXSIZE)
// active entries are eligible for automatic refresh once the specified time duration has
// elapsed after the entry was last modified.
.refreshAfterWrite(timeoutMs, TimeUnit.MILLISECONDS)
// each entry should be automatically removed from the cache once the specified time
// duration has elapsed after the entry was last modified.
.expireAfterWrite(10 * timeoutMs, TimeUnit.MILLISECONDS)
.build(new GroupMappingCacheLoader());
}
}
/**
* Class to reload cache from underlying user group mapping service implementation.
*/
private class GroupMappingCacheLoader extends CacheLoader<String, List<String>> {
/**
* Constructs a new {@link GroupMappingCacheLoader}.
*/
public GroupMappingCacheLoader() {}
@Override
public List<String> load(String user) throws IOException {
return mService.getGroups(user);
}
@Override
public ListenableFuture<List<String>> reload(final String user, List<String> oldValue)
throws IOException {
// Load values asynchronously.
ListenableFuture<List<String>> listenableFuture = mExecutorService.submit(
new Callable<List<String>>() {
@Override
public List<String> call() throws IOException {
return load(user);
}
}
);
return listenableFuture;
}
}
/**
* Gets a list of groups for the given user.
*
* @param user user name
* @return the list of groups that the user belongs to
*/
public List<String> getGroups(String user) throws IOException {
if (!mCacheEnabled) {
return mService.getGroups(user);
}
try {
return mCache.get(user);
} catch (ExecutionException e) {
throw new IOException(e);
}
}
}