Package org.activiti.explorer.cache

Source Code of org.activiti.explorer.cache.TrieBasedUserCache

/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.activiti.explorer.cache;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.activiti.engine.IdentityService;
import org.activiti.engine.identity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;



/**
* Simple cache of user information, to avoid hitting the database too often for
* information that doesn't change much over time.
*
* Based on a Trie datastructure (http://en.wikipedia.org/wiki/Trie), see {@link RadixTree},
* for fast 'telephonebook'-like retrieval based on the first and last name of the users.
* Note that we are using the Trie such that we can have multiple results for a given key, by
* giving each key a list of matching values:
* eg. key='kermit' has a list of values {Kermit The Frog, Kermit The Evil Overlord, ...}
*
* TODO: In a clustered/cloud environment, this cache must be refreshed each xx minutes,
* in case updates have been done on other machines. Alternatively, a solution
* such as memcached could replace this implementation later on.
*
* @author Joram Barrez
*/
@Component
public class TrieBasedUserCache implements UserCache {
 
  private static final Logger LOGGER = Logger.getLogger(TrieBasedUserCache.class.getName());
 
  protected IdentityService identityService;
  protected RadixTree<List<User>> userTrie = new RadixTreeImpl<List<User>>();
  protected Map<String, List<String>> keyCache = new HashMap<String, List<String>>();
  protected Map<String, User> userCache = new HashMap<String, User>();
 
  public void refresh() {
    userTrie = new RadixTreeImpl<List<User>>();
    loadUsers();
  }
 
  public synchronized void loadUsers() {
    long nrOfUsers = identityService.createUserQuery().count();
    long usersAdded = 0;
   
    userTrie = new RadixTreeImpl<List<User>>();
    userCache = new HashMap<String, User>();
    keyCache = new HashMap<String, List<String>>();
   
    while (usersAdded < nrOfUsers) {

      if (LOGGER.isLoggable(Level.INFO)) {
        LOGGER.info("Caching users " + usersAdded + " to " + (usersAdded+25));
      }
     
      List<User> users = identityService.createUserQuery().listPage((int) usersAdded, 25);
      for (User user : users) {
        addTrieItem(user);
        addUserCacheItem(user);
       
        usersAdded++;
      }
    }
  }

  protected void addTrieItem(User user) {
    for (String key : getKeys(user)) {
      addTrieCacheItem(key, user);
    }
  }
 
  protected String[] getKeys(User user) {
    String fullname = "";
    if (user.getFirstName() != null) {
      fullname += user.getFirstName();
    }
    if (user.getLastName() != null) {
      fullname += " " + user.getLastName();
    }
   
    return fullname.split(" ");
  }
 
  protected void addTrieCacheItem(String key, User user) {
    key = key.toLowerCase();

    // Trie update
    List<User> value = null;
    if (!userTrie.contains(key)) {
      value = new ArrayList<User>();
    } else {
      value = userTrie.find(key);
    }
   
    value.add(user);
    userTrie.delete(key);
    userTrie.insert(key, value);
   
    // Key map update
    if (!keyCache.containsKey(user.getId())) {
      keyCache.put(user.getId(), new ArrayList<String>());
    }
    keyCache.get(user.getId()).add(key);
  }
 
  protected void addUserCacheItem(User user) {
    userCache.put(user.getId(), user);
  }
 
  public User findUser(String userId) {
    if (userCache.isEmpty()) {
      loadUsers();
    }
    return userCache.get(userId);
  }
 
  public List<User> findMatchingUsers(String prefix) {
   
    if (userTrie.getSize() == 0) {
      refresh();
    }
   
    List<User> returnValue = new ArrayList<User>();
    List<List<User>> results = userTrie.searchPrefix(prefix.toLowerCase(), 100); // 100 should be enough for any name
    for (List<User> result : results) {
      for (User userDetail : result) {
        returnValue.add(userDetail);
      }
    }
    return returnValue;
  }
 
  public void notifyUserDataChanged(String userId) {
    User newData = identityService.createUserQuery().userId(userId).singleResult();
   
    // Update user trie: first remove old values
    if (keyCache.containsKey(userId)) {
      for (String key : keyCache.get(userId)) {
        List<User> users = userTrie.find(key);
        if (users != null && !users.isEmpty()) {
          Iterator<User> userIterator = users.iterator();
          while (userIterator.hasNext()) {
            User next = userIterator.next();
            if (next.getId().equals(userId)) {
              userIterator.remove();
            }
          }
        }
      }
    }
   
    // Update key cache
    keyCache.remove(userId);
   
    if (newData != null) {
      // Update user trie: add new value
      addTrieItem(newData);
     
      // Update user cache
      userCache.put(newData.getId(), newData);
    }
  }

  @Autowired
  public void setIdentityService(IdentityService identityService) {
    this.identityService = identityService;
    loadUsers();
  }
 
}
TOP

Related Classes of org.activiti.explorer.cache.TrieBasedUserCache

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.