/******************************************************************************* * Copyright (c) 2014, 2016 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.orion.internal.server.core.metastore; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.orion.server.core.ServerConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A cache for various user properties in a {@code SimpleMetaStore}. It is expensive to go to disk * to search for users matching a particular property, such as oauth or email address. * This user property cache is used by the {@link SimpleMetaStore#registerUserProperty(String) registerUserProperty} * and {@link SimpleMetaStore#readUserByProperty(String, String, boolean, boolean) readUserByProperty} to quickly * locate users. * * @author Anthony Hunter */ public class SimpleMetaStoreUserPropertyCache { Logger logger = LoggerFactory.getLogger(SimpleMetaStoreUserPropertyCache.class); //$NON-NLS-1$ /** * A map of the user caches keyed by the property key. */ private Map<String, Map<String, String>> cacheMap = new ConcurrentHashMap<String, Map<String, String>>(); public SimpleMetaStoreUserPropertyCache() { } public void register(List<String> propertyKeys) throws CoreException { for (String propertyKey : propertyKeys) { if (cacheMap.containsKey(propertyKey)) { throw new CoreException(new Status(IStatus.ERROR, ServerConstants.PI_SERVER_CORE, 1, "SimpleMetaStoreUserPropertyCache.registerUserProperty: property " + propertyKey + " is already registered", null)); } Map<String, String> cache = new ConcurrentHashMap<String, String>(); cacheMap.put(propertyKey, cache); if (logger.isDebugEnabled()) { logger.debug("Created user cache for the " + propertyKey + " property"); //$NON-NLS-1$ } } } public boolean isRegistered(String propertyKey) { return cacheMap.containsKey(propertyKey); } public void add(String propertyKey, String value, String userId) { Map<String, String> cache = cacheMap.get(propertyKey); if (cache == null) { return; } if (cache.containsValue(userId)) { String key = null; for (Entry<String, String> entry : cache.entrySet()) { if (userId.equals(entry.getValue())) { key = entry.getKey(); } } if (key != null) { cache.remove(key); if (logger.isDebugEnabled()) { logger.debug("Removed " + key + " for the user " + userId + " from the user cache for the " + propertyKey + " property"); //$NON-NLS-1$ } } } cache.put(value, userId); if (logger.isDebugEnabled()) { logger.debug("Added " + value + " for the user " + userId + " to the user cache for the " + propertyKey + " property"); //$NON-NLS-1$ } } public void deleteUser(String userId) { for (String propertyKey : cacheMap.keySet()) { Map<String, String> cache = cacheMap.get(propertyKey); if (cache.containsValue(userId)) { String key = null; for (Entry<String, String> entry : cache.entrySet()) { if (userId.equals(entry.getValue())) { key = entry.getKey(); } } if (key != null) { cache.remove(key); if (logger.isDebugEnabled()) { logger.debug("Removed " + key + " for the user " + userId + " from the user cache for the " + propertyKey + " property"); //$NON-NLS-1$ } } } } } public String readUserByProperty(String key, String value, boolean regExp, boolean ignoreCase) throws CoreException { if (!cacheMap.containsKey(key)) { throw new CoreException(new Status(IStatus.ERROR, ServerConstants.PI_SERVER_CORE, 1, "SimpleMetaStore.registerUserProperty: property " + key + " is not registered", null)); } Map<String, String> cache = cacheMap.get(key); Pattern pattern = regExp ? Pattern.compile(value, Pattern.MULTILINE | Pattern.DOTALL) : null; // Use the cache to lookup the user for the specified property value for (Map.Entry<String, String> entry : cache.entrySet()) { String cacheKey = entry.getKey(); String userId = entry.getValue(); boolean hasMatch; if (pattern != null) { hasMatch = pattern.matcher(cacheKey).matches(); } else { hasMatch = ignoreCase ? cacheKey.equalsIgnoreCase(value) : cacheKey.equals(value); } if (hasMatch) { return userId; } } return null; } public void setProperties(String userId, Map<String, String> properties) { for (String key : properties.keySet()) { String value = properties.get(key); if (cacheMap.containsKey(key)) { Map<String, String> cache = cacheMap.get(key); if (cache.containsValue(userId)) { String oldValue = null; for (Entry<String, String> entry : cache.entrySet()) { if (userId.equals(entry.getValue())) { oldValue = entry.getKey(); } } if (oldValue != null) { cache.remove(oldValue); if (logger.isDebugEnabled()) { logger.debug("Removed " + oldValue + " for the user " + userId + " to the user cache for the " + key + " property"); //$NON-NLS-1$ } } } cache.put(value, userId); if (logger.isDebugEnabled()) { logger.debug("Added " + value + " for the user " + userId + " to the user cache for the " + key + " property"); //$NON-NLS-1$ } } } } public void delete(String key, String value, String userId) { Map<String, String> cache = cacheMap.get(key); cache.remove(value); if (logger.isDebugEnabled()) { logger.debug("Deleted " + value + " for the user " + userId + " from the user cache for the " + key + " property"); //$NON-NLS-1$ } } }