Java tutorial
/* * This file is part of the RUNA WFE project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ package ru.runa.wfe.user.cache; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang.SerializationUtils; import org.hibernate.Criteria; import org.hibernate.Session; import ru.runa.wfe.commons.ApplicationContextFactory; import ru.runa.wfe.commons.cache.BaseCacheImpl; import ru.runa.wfe.commons.cache.Cache; import ru.runa.wfe.commons.cache.CacheImplementation; import ru.runa.wfe.commons.cache.Change; import ru.runa.wfe.commons.cache.ChangedObjectParameter; import ru.runa.wfe.commons.cache.VersionedCacheData; import ru.runa.wfe.commons.cache.sm.CacheInitializationProcessContext; import ru.runa.wfe.commons.cache.sm.CacheInitializationProcessContextStub; import ru.runa.wfe.presentation.BatchPresentation; import ru.runa.wfe.user.Actor; import ru.runa.wfe.user.Executor; import ru.runa.wfe.user.ExecutorGroupMembership; import ru.runa.wfe.user.Group; /** * Cache for executors. * * @author Konstantinov Aleksey */ class ExecutorCacheImpl extends BaseCacheImpl implements ManageableExecutorCache { /* Ehcaches names. */ public static final String actorsByCodesName = "ru.runa.wfe.user.cache.actorsByCodes"; public static final String executorsByIdName = "ru.runa.wfe.user.cache.executorsById"; public static final String executorsByNameName = "ru.runa.wfe.user.cache.executorsByName"; public static final String groupMembersName = "ru.runa.wfe.user.cache.groupMembers"; public static final String executorParentsName = "ru.runa.wfe.user.cache.executorParents"; public static final String allGroupActorsName = "ru.runa.wfe.user.cache.allGroupActors"; public static final String allExecutorGroupsName = "ru.runa.wfe.user.cache.allExecutorGroups"; public static final String allExecutorsListsName = "ru.runa.wfe.user.cache.allExecutorsLists"; /* Caches implementation. */ private final Cache<Long, Actor> codeToActorCache; private final Cache<Long, Executor> idToExecutorCache; private final Cache<String, Executor> nameToExecutorCache; private final Cache<Long, HashSet<Executor>> groupToMembersCache; private final Cache<Long, HashSet<Group>> executorToParentGroupsCache; private final Cache<Long, HashSet<Actor>> groupToAllActorMembersCache; private final Cache<Long, HashSet<Group>> executorToAllParentGroupsCache; private final Cache<Class<?>, ConcurrentHashMap<BatchPresentationFieldEquals, List<Executor>>> batchAllExecutors; public ExecutorCacheImpl() { this(new CacheInitializationProcessContextStub()); } public ExecutorCacheImpl(CacheInitializationProcessContext context) { codeToActorCache = createCache(actorsByCodesName); idToExecutorCache = createCache(executorsByIdName); nameToExecutorCache = createCache(executorsByNameName); groupToMembersCache = createCache(groupMembersName); executorToParentGroupsCache = createCache(executorParentsName); groupToAllActorMembersCache = createCache(allGroupActorsName); executorToAllParentGroupsCache = createCache(allExecutorGroupsName); batchAllExecutors = createCache(allExecutorsListsName); List<Executor> allExecutors = getAllExecutors(); if (!context.isInitializationStillRequired()) { return; } List<ExecutorGroupMembership> memberships = getAllMemberships(); if (!context.isInitializationStillRequired()) { return; } for (Executor executor : allExecutors) { addExecutorToCaches(executor); if (!context.isInitializationStillRequired()) { return; } } fillGroupMembersCaches(context, memberships, allExecutors); } @Override public Actor getActor(Long code) { return (Actor) SerializationUtils.clone(codeToActorCache.get(code)); } @Override public Executor getExecutor(String name) { return (Executor) SerializationUtils.clone(nameToExecutorCache.get(name)); } @Override public Executor getExecutor(Long id) { return (Executor) SerializationUtils.clone(idToExecutorCache.get(id)); } @Override public Set<Executor> getGroupMembers(Group group) { return (Set<Executor>) SerializationUtils.clone(groupToMembersCache.get(group.getId())); } @Override public Set<Actor> getGroupActorsAll(Group group) { return (Set<Actor>) SerializationUtils.clone(groupToAllActorMembersCache.get(group.getId())); } @Override public Set<Group> getExecutorParents(Executor executor) { return (Set<Group>) SerializationUtils.clone(executorToParentGroupsCache.get(executor.getId())); } @Override public Set<Group> getExecutorParentsAll(Executor executor) { return (Set<Group>) SerializationUtils.clone(executorToAllParentGroupsCache.get(executor.getId())); } @Override public <T extends Executor> VersionedCacheData<List<T>> getAllExecutor(Class<T> clazz, BatchPresentation batch) { synchronized (this) { ConcurrentHashMap<BatchPresentationFieldEquals, List<Executor>> map = batchAllExecutors.get(clazz); if (map == null) { return getVersionnedData(null); } List<Executor> cachedExecutors = map.get(new BatchPresentationFieldEquals(batch)); if (cachedExecutors == null) { return getVersionnedData(null); } return getVersionnedData((List<T>) Collections.unmodifiableList(cachedExecutors)); } } @Override public <T extends Executor> void addAllExecutor(VersionedCacheData<List<T>> oldCachedData, Class<?> clazz, BatchPresentation batch, List<T> executors) { if (!mayUpdateVersionnedData(oldCachedData)) { return; } synchronized (this) { ConcurrentHashMap<BatchPresentationFieldEquals, List<Executor>> map = batchAllExecutors.get(clazz); if (map == null) { map = new ConcurrentHashMap<BatchPresentationFieldEquals, List<Executor>>(); } List<Executor> result = new ArrayList<Executor>(); for (Executor executor : executors) { result.add(executor); } map.put(new BatchPresentationFieldEquals(batch), result); batchAllExecutors.put(clazz, map); } } public <T extends Executor> boolean onExecutorChange(String executorName, Class<T> executorClass, boolean createOrDelete) { Executor executor = nameToExecutorCache.get(executorName); if (executor == null) { return true; } nameToExecutorCache.remove(executor.getName()); idToExecutorCache.remove(executor.getId()); if (executor instanceof Actor) { codeToActorCache.remove(((Actor) executor).getCode()); } batchAllExecutors.clear(); Set<Group> upperGroups = executorToAllParentGroupsCache.get(executor.getId()); if (upperGroups != null) { for (Group upperGroup : upperGroups) { groupToMembersCache.remove(upperGroup.getId()); groupToAllActorMembersCache.remove(upperGroup.getId()); } } return clearGroupMembersCaches(executor); } private boolean clearGroupMembersCaches(Executor executor) { boolean result = true; result = result && executorToAllParentGroupsCache.remove(executor.getId()); result = result && executorToParentGroupsCache.remove(executor.getId()); if (executor instanceof Group) { Set<Executor> executors = groupToMembersCache.get(executor.getId()); if (executors != null) { for (Executor ex : executors) { result = result && clearGroupMembersCaches(ex); } } } return result; } public boolean onGroupMembersChange(Group group) { boolean result = true; groupToMembersCache.remove(group.getId()); groupToAllActorMembersCache.remove(group.getId()); batchAllExecutors.clear(); Set<Group> upperGroups = executorToAllParentGroupsCache.get(group.getId()); if (upperGroups != null) { for (Group upperGroup : upperGroups) { groupToMembersCache.remove(upperGroup.getId()); groupToAllActorMembersCache.remove(upperGroup.getId()); } } return result; } public boolean onExecutorInGroupChange(Executor executor) { batchAllExecutors.clear(); return clearGroupMembersCaches(executor); } private void addExecutorToCaches(Executor executor) { idToExecutorCache.put(executor.getId(), executor); nameToExecutorCache.put(executor.getName(), executor); if (executor instanceof Actor) { codeToActorCache.put(((Actor) executor).getCode(), (Actor) executor); } } private <Key extends Serializable, ValueInSet> Set<ValueInSet> getCollectionFromMap( Cache<Key, HashSet<ValueInSet>> map, Key key) { HashSet<ValueInSet> retVal = map.get(key); if (retVal == null) { retVal = new HashSet<ValueInSet>(); map.put(key, retVal); } return retVal; } private void fillGroupMembersCaches(CacheInitializationProcessContext context, List<ExecutorGroupMembership> memberships, List<Executor> executors) { for (ExecutorGroupMembership membership : memberships) { getCollectionFromMap(groupToMembersCache, membership.getGroup().getId()).add(membership.getExecutor()); getCollectionFromMap(executorToParentGroupsCache, membership.getExecutor().getId()) .add(membership.getGroup()); if (!context.isInitializationStillRequired()) { return; } } for (Executor executor : executors) { if (executorToParentGroupsCache.get(executor.getId()) == null) { executorToParentGroupsCache.put(executor.getId(), new HashSet<Group>()); } if (executor instanceof Group && groupToMembersCache.get(executor.getId()) == null) { groupToMembersCache.put(executor.getId(), new HashSet<Executor>()); } if (!context.isInitializationStillRequired()) { return; } } for (Executor executor : executors) { fillAllParentsCache(executorToAllParentGroupsCache, nameToExecutorCache.get(executor.getName()), executorToParentGroupsCache); if (executor instanceof Group) { fillActorMembersCache(groupToAllActorMembersCache, (Group) (nameToExecutorCache.get(executor.getName())), groupToMembersCache); } if (!context.isInitializationStillRequired()) { return; } } } private Set<Group> fillAllParentsCache(Cache<Long, HashSet<Group>> cache, Executor executor, Cache<Long, HashSet<Group>> mapExecutorToParents) { HashSet<Group> executorGroups = cache.get(executor.getId()); if (executorGroups != null) { return executorGroups; } executorGroups = new HashSet<Group>(); cache.put(executor.getId(), executorGroups); if (mapExecutorToParents.get(executor.getId()) != null) { for (Group group : mapExecutorToParents.get(executor.getId())) { executorGroups.add(group); executorGroups.addAll(fillAllParentsCache(cache, group, mapExecutorToParents)); } } return executorGroups; } private Set<Actor> fillActorMembersCache(Cache<Long, HashSet<Actor>> cache, Group group, Cache<Long, HashSet<Executor>> mapGroupToMembers) { HashSet<Actor> actorMembers = cache.get(group.getId()); if (actorMembers != null) { return actorMembers; } actorMembers = new HashSet<Actor>(); cache.put(group.getId(), actorMembers); if (mapGroupToMembers.get(group.getId()) != null) { for (Executor ex : mapGroupToMembers.get(group.getId())) { if (ex instanceof Actor) { actorMembers.add((Actor) ex); } else { actorMembers.addAll(fillActorMembersCache(cache, (Group) ex, mapGroupToMembers)); } } } return actorMembers; } private <T> List<T> getAll(Class<?> clazz) { Session session = ApplicationContextFactory.getCurrentSession(); Criteria criteria = session.createCriteria(clazz); return criteria.list(); } private List<ExecutorGroupMembership> getAllMemberships() { return getAll(ExecutorGroupMembership.class); } private List<Executor> getAllExecutors() { return getAll(Executor.class); } private static class BatchPresentationFieldEquals { private final BatchPresentation batchPresentation; BatchPresentationFieldEquals(BatchPresentation batchPresentation) { this.batchPresentation = batchPresentation.clone(); } @Override public boolean equals(Object obj) { if (obj instanceof ExecutorCacheImpl.BatchPresentationFieldEquals) { return batchPresentation .fieldEquals(((ExecutorCacheImpl.BatchPresentationFieldEquals) obj).batchPresentation); } return false; } @Override public int hashCode() { return batchPresentation.hashCode(); } } @Override public CacheImplementation unlock() { return null; } @Override public boolean onChange(ChangedObjectParameter changedObject) { if (changedObject.object instanceof Executor) { boolean cleared = false; int idx = changedObject.getPropertyIndex("name"); boolean createOrDelete = changedObject.changeType == Change.CREATE || changedObject.changeType == Change.DELETE; if (changedObject.object instanceof Actor) { cleared = onExecutorChange((String) changedObject.currentState[idx], Actor.class, createOrDelete); if (changedObject.previousState != null) { cleared = cleared && onExecutorChange((String) changedObject.previousState[idx], Actor.class, createOrDelete); } } else { cleared = onExecutorChange((String) changedObject.currentState[idx], Executor.class, createOrDelete); if (changedObject.previousState != null) { cleared = cleared && onExecutorChange((String) changedObject.previousState[idx], Executor.class, createOrDelete); } } if (!cleared) { return false; } return true; } if (changedObject.object instanceof ExecutorGroupMembership) { boolean cleared = true; ExecutorGroupMembership membership = (ExecutorGroupMembership) changedObject.object; cleared = cleared && onExecutorInGroupChange(membership.getExecutor()); cleared = cleared && onGroupMembersChange(membership.getGroup()); if (!cleared) { return false; } return true; } return false; } }