Mercurial > hg > openmind
view src/main/java/org/mpi/openmind/cache/AbstractCacheService.java @ 75:e0be7c0030f5
cleanup and better comments.
author | casties |
---|---|
date | Thu, 23 Feb 2017 19:05:47 +0100 |
parents | 677492395dc0 |
children |
line wrap: on
line source
package org.mpi.openmind.cache; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import org.mpi.openmind.repository.bo.Attribute; import org.mpi.openmind.repository.bo.Entity; import org.mpi.openmind.repository.bo.Node; import org.mpi.openmind.repository.bo.Relation; import org.mpi.openmind.repository.bo.ViewerAttribute; import org.mpi.openmind.repository.bo.ViewerPage; import org.mpi.openmind.repository.services.PersistenceService; import cl.maps.duplex.DuplexKey; import cl.maps.duplex.DuplexMap; import cl.maps.penta.PentaMap; import cl.maps.triple.TripleMap; import cl.maps.utils.AttKey; import cl.maps.utils.RelKey; public abstract class AbstractCacheService{ private static Logger logger = Logger.getLogger(AbstractCacheService.class); private PersistenceService persistenceService; protected DuplexMap<Entity, String, Long> entMap; private Map<String, Boolean> entLoadedByOC; public AbstractCacheService(){ logger.info( "[" + Thread.currentThread().getId() + "]\tInitializing AbstractCacheService HashCode=" + this.hashCode()); //Initializing entities structures this.entMap = new DuplexMap<Entity, String, Long>(); this.entLoadedByOC = new HashMap<String, Boolean>(); //Initializing attributes structures this.attMap = new TripleMap<Attribute, AttKey, Long, Long>(); this.attLoadedByOCAndName = new HashMap<AttKey, Boolean>(); this.attLoadedBySrcId = new HashMap<Long, Boolean>(); //Initializing relations structures this.relMap = new PentaMap<Relation, RelKey, Long, Long, String, Long>(); this.relLoadedBySrcId = new HashMap<Long, Boolean>(); this.relLoadedByTarId = new HashMap<Long, Boolean>(); this.relLoadedByName = new HashMap<String, Boolean>(); } /** * Remove the entity from the cache. * * @param entId * @param oc * @return */ protected Entity removeEntity(Long entId, String oc){ return this.entMap.remove(new DuplexKey<String, Long>(oc, entId)); } /** * Put the entity in the cache. * * @param ent */ protected void updateLWEntity(Entity ent) { // update access timestamp ent.setAccessTime(System.currentTimeMillis()); Entity oldEnt = this.entMap.put(ent.getKey(), ent); if (oldEnt != null && oldEnt.getAccessTime() == ent.getAccessTime()) { logger.error("Cache updated with entity with same accessTime! old="+oldEnt+" new="+ent); } } /** * Returns (a clone of) an entity from the cache by id. * * @param id * @return */ public Entity getEntityById(Long id) { Entity ent = getEntityByIdReadOnly(id); if (ent != null) { return (Entity) ent.clone(); } return null; } /** * Returns an entity from the cache by id. * * Loads the entity from the database if its not in the cache. * * Do not modify the entity! * * @param id * @return */ public Entity getEntityByIdReadOnly(Long id) { if (id == null) { logger.error("ID of entity is null!"); } else { // get entity from cache Entity ent = this.entMap.getValuesByOwnKey(id); if (ent == null) { // load entity from db logger.debug("Entity no found in cache ID=" + id); List<Entity> list = getPs().getLightweightEntities(Node.SYS_STATUS_CURRENT_VERSION, id, Node.TYPE_ABOX, null, null, true, 1); if (list.size() > 0) { ent = list.get(0); // load all entities of the same object class loadEntitiesByDef(ent.getObjectClass()); // TODO: should we use the newly loaded entity? } } return ent; } return null; } /** * Load all entities of the given object class into the cache. * * @param oc * @return */ public List<Entity> loadEntitiesByDef(String oc) { logger.debug( "[" + Thread.currentThread().getId() + "] getEntitiesByDef=" + oc + " HashCode=" + this.hashCode()); if (entLoadedByOC.get(oc) == null || entLoadedByOC.get(oc) == false) { synchronized (entMap) { if (entLoadedByOC.get(oc) == null) { long start = System.currentTimeMillis(); logger.debug("[" + Thread.currentThread().getId() + "] Starting loading Entities from DB for OC=" + oc + " HashCode=" + this.hashCode()); entLoadedByOC.put(oc, false); // load entities from database List<Entity> list = getPs().getLightweightEntities(Node.SYS_STATUS_CURRENT_VERSION, null, Node.TYPE_ABOX, oc, null, true, -1); // put entities in cache for (Entity ent : list) { // set access timestamp ent.setAccessTime(System.currentTimeMillis()); entMap.put(ent.getKey(), ent); } entLoadedByOC.put(oc, true); long end = System.currentTimeMillis(); logger.debug("[" + Thread.currentThread().getId() + "] Finished loading Entities from DB for OC=" + oc + " - time= " + (end - start) + " HashCode=" + this.hashCode()); } } } List<Entity> list = entMap.getValuesByAKey(oc); return list; } /** * Saves the Entities (without content) in the database and puts them in the cache. * * @param nodeList * @throws Exception */ public void saveEntityListAsNodeWithoutContent(List<Entity> nodeList) throws Exception{ this.getPs().saveEntityListAsNodeWithoutContent(nodeList); for(Entity ent : nodeList){ // update access timestamp ent.setAccessTime(System.currentTimeMillis()); entMap.put(ent.getKey(), ent); } } /** * Check if the given Entity is up-to-date with the cache. * * Compares accessTime only. * * @param ent * @return */ public boolean isLWEntityCurrent(Entity ent) { Entity cacheEnt = this.entMap.getValuesByOwnKey(ent.getId()); if (cacheEnt == null) { // not in cache return true; } long ct = cacheEnt.getAccessTime(); long et = ent.getAccessTime(); if (ct == 0 || et == 0 || et > ct) { logger.warn("Weird cache access times! old="+Long.toString(ct)+" new="+Long.toString(et)); } return (ct == et); } //****************************************** //************Attributes******************** //****************************************** //AttKey(OC, attName), SrcId protected TripleMap<Attribute, AttKey, Long, Long> attMap; private Map<Long, Boolean> attLoadedBySrcId; private Map<AttKey, Boolean> attLoadedByOCAndName; protected boolean removeAtt(Long attId){ Attribute att = this.attMap.getValuesByOwnKey(attId); if(att != null){ this.attMap.remove(att.getKey()); return true; } return false; } protected boolean removeAtt(Attribute att){ if(att != null){ this.attMap.remove(att.getKey()); return true; } return false; } /** * Put attribute in cache. * * @param att */ protected void updateAtt(Attribute att){ // update access timestamp att.setAccessTime(System.currentTimeMillis()); this.attMap.put(att.getKey(), att); } /** * Put list of attributes in cache. * * @param list */ protected void updateAttList(List<Attribute> list){ for(Attribute att : list){ // update access timestamp att.setAccessTime(System.currentTimeMillis()); this.attMap.put(att.getKey(), att); } } /** * Return a list of (cloned) Attributes with the given source id. * * Loads Attributes from the database into the cache. * * @param entId * @return */ public List<Attribute> getAttsBySrcId(Long entId){ List<Attribute> list = new ArrayList<Attribute>(); for(Attribute att : getAttsBySrcIdReadOnly(entId)){ list.add((Attribute)att.clone()); } return list; } /** * Return a list of (non-cloned) Attributes with the given source id from the cache. * * Loads Attributes from the database into the cache. * * @param srcId * @return */ public List<Attribute> getAttsBySrcIdReadOnly(Long srcId) { if(attLoadedBySrcId.get(srcId) == null || attLoadedBySrcId.get(srcId) == false){ synchronized (attMap) { if(attLoadedBySrcId.get(srcId) == null) { attLoadedBySrcId.put(srcId, false); // load from db List<Attribute> list = getPs().getAllAttributes(srcId, -1); for (Attribute att : list) { // update access timestamp att.setAccessTime(System.currentTimeMillis()); // put in cache this.attMap.put(att.getKey(), att); } attLoadedBySrcId.put(srcId, true); } } } // return attributes from cache return this.attMap.getValuesByBKey(srcId); } public List<Attribute> getAttsByOCAndName(String oc, String attName){ AttKey ocNameKey = new AttKey(oc, attName); //logger.info("[" + Thread.currentThread().getId() + // "]\tgetAttsByOCAndNames oc=" + oc + " - attName=" + attName + "\nHashCode=" + this.hashCode()); if(attLoadedByOCAndName.get(ocNameKey) == null || attLoadedByOCAndName.get(ocNameKey) == false){ synchronized (attMap) { if(attLoadedByOCAndName.get(ocNameKey) == null){ logger.info("STARTING getAttsByOCAndNames oc=" + oc + " - attName=" + attName); attLoadedByOCAndName.put(ocNameKey, false); List<Attribute> list = getPs().getAttributeByDefByName(oc, attName, -1); for(Attribute att : list){ // update access timestamp att.setAccessTime(System.currentTimeMillis()); this.attMap.put(att.getKey(), att); } attLoadedByOCAndName.put(ocNameKey, true); logger.info("FINISHED getAttsByOCAndNames oc=" + oc + " - attName=" + attName + " - size=" + list.size()); } } } return this.attMap.getValuesByAKey(ocNameKey); } /** * Check if the given Attribute is up-to-date with the cache. * * Compares accessTime only. * * @param att * @return */ public boolean isAttCurrent(Attribute att) { Attribute cacheAtt = this.attMap.get(att.getKey()); if (cacheAtt == null) { // not in cache return true; } long ct = cacheAtt.getAccessTime(); long et = att.getAccessTime(); if (ct == 0 || et == 0 || et > ct) { logger.warn("Weird cache access times! old="+Long.toString(ct)+" new="+Long.toString(et)); } return (ct == et); } //****************************************** //************Relations********************* //****************************************** //RelKey<>, src, tar, relId protected PentaMap<Relation, RelKey, Long, Long, String, Long> relMap; private Map<Long, Boolean> relLoadedBySrcId; private Map<Long, Boolean> relLoadedByTarId; private Map<String, Boolean> relLoadedByName; protected boolean removeRel(Long relId){ Relation rel = this.relMap.getValuesByOwnKey(relId); if(rel != null){ this.relMap.remove(rel.getKey()); return true; } return false; } protected boolean removeRel(Relation rel){ if(rel != null){ this.relMap.remove(rel.getKey()); return true; } return false; } /** * Put Relation in cache. * * @param rel */ protected void updateRel(Relation rel){ // update access timestamp rel.setAccessTime(System.currentTimeMillis()); this.relMap.put(rel.getKey(), rel); } /** * Put list of Relations in cache. * * @param list */ protected void updateRelList(List<Relation> list){ for(Relation rel : list){ // update access timestamp rel.setAccessTime(System.currentTimeMillis()); this.relMap.put(rel.getKey(), rel); } } /** * Return Relations with matching source id from cache. * * Loads Relations from db and stores in cache if not in cache. * * @param srcId * @return * @throws Exception */ public List<Relation> getRelsBySrcId(long srcId) throws Exception { if (relLoadedBySrcId.get(srcId) == null || relLoadedBySrcId.get(srcId) == false) { // Relation not in cache synchronized (relMap) { if (relLoadedBySrcId.get(srcId) == null) { relLoadedBySrcId.put(srcId, false); List<Relation> list = getPs().getSourceRelationsFromDB(this.getEntityByIdReadOnly(srcId), null, null, -1, false); for (Relation rel : list) { // update access timestamp rel.setAccessTime(System.currentTimeMillis()); this.relMap.put(rel.getKey(), rel); } relLoadedBySrcId.put(srcId, true); } } } return relMap.getValuesByBKey(srcId); } /** * Return Relations with matching target id from cache. * * Loads Relations from db and stores in cache if not in cache. * * @param tarId * @return */ public List<Relation> getRelsByTarId(Long tarId){ if(relLoadedByTarId.get(tarId) == null || relLoadedByTarId.get(tarId) == false){ synchronized (relMap) { if(relLoadedByTarId.get(tarId) == null){ //logger.info("getRelsByTarId fromDB tarId=" + tarId); relLoadedByTarId.put(tarId, false); List<Relation> list = getPs().getTargetRelationsFromDB(this.getEntityByIdReadOnly(tarId), null, null, -1, false); for(Relation rel : list){ // update access timestamp rel.setAccessTime(System.currentTimeMillis()); this.relMap.put(rel.getKey(), rel); } relLoadedByTarId.put(tarId, true); } } } return relMap.getValuesByCKey(tarId); } /** * TODO the relations returned by this method have no attributes. * 'cause the performance. * There are to ways to correct this issue: * 1) load the attributes directly from the DB (slow solution) * 2) use LW state for the relation, like the entities. * * It should not mean a problem, because the other methods to get relations load the relation with attributes. * Only getting relation one by one (using ID) would be a problem, but this method does not exist. * * @param name * @return */ public List<Relation> getRelsByName(String name) throws Exception{ if(relLoadedByName.get(name) == null || relLoadedByName.get(name) == false){ synchronized (relMap) { if(relLoadedByName.get(name) == null){ logger.info("getRelsByName fromDB name=" + name); relLoadedByName.put(name, false); List<Relation> list = getPs().getRelationByNameDB(name, -1); for(Relation rel : list){ // update access timestamp rel.setAccessTime(System.currentTimeMillis()); this.relMap.put(rel.getKey(), rel); } relLoadedByName.put(name, true); } } } return relMap.getValuesByDKey(name); } /** * Check if the given Relation is up-to-date with the cache. * * Compares accessTime only. * * @param rel * @return */ public boolean isRelCurrent(Relation rel) { Relation cacheRel = this.relMap.get(rel.getKey()); if (cacheRel == null) { // not in cache return true; } long ct = cacheRel.getAccessTime(); long et = rel.getAccessTime(); if (ct == 0 || et == 0 || et > ct) { logger.warn("Weird cache access times! old="+Long.toString(ct)+" new="+Long.toString(et)); } return (ct == et); } public PersistenceService getPs() { return persistenceService; } public void setPs(PersistenceService ps){ this.persistenceService = ps; } //*********************************** //*********************************** //*********************************** //*********************************** private Map<Long, ViewerPage> viewerPageMap; private DuplexMap<ViewerAttribute, Long, Long> viewerAttMap; protected Map<Long, ViewerPage> getViewerPageMap(){ if(viewerPageMap == null){ this.loadViewerPageMap(); } return viewerPageMap; } protected DuplexMap<ViewerAttribute, Long, Long> getViewerAttMap(){ if(viewerAttMap == null){ this.loadViewerAttMap(); } return viewerAttMap; } public int removeViewerPage(Long id){ int rows = 0; if(viewerPageMap == null){ this.loadViewerPageMap(); } synchronized (viewerPageMap) { viewerPageMap.remove(id); rows += getPs().removeViewerPage(id); List<ViewerAttribute> attrs = getViewerAttMap().getValuesByAKey(id); for(ViewerAttribute attr : attrs){ rows += removeViewerAttribute(attr.getId()); } } return rows; } public int removeViewerAttribute(Long id){ int rows = 0; if(viewerAttMap == null){ this.loadViewerAttMap(); } synchronized (viewerAttMap) { ViewerAttribute att = viewerAttMap.getValuesByOwnKey(id); if(att != null){ viewerAttMap.remove(att.getKey()); rows = getPs().removeViewerAttribute(att.getId()); } } return rows; } private void loadViewerAttMap(){ if(viewerAttMap == null){ viewerAttMap = new DuplexMap<ViewerAttribute, Long, Long>(); synchronized (viewerAttMap) { List<ViewerAttribute> list = getPs().getViewerAttributes(); for(ViewerAttribute att : list){ viewerAttMap.put(att.getKey(), att); } } } } private void loadViewerPageMap(){ if(viewerPageMap == null){ viewerPageMap = new HashMap<Long, ViewerPage>(); synchronized (viewerPageMap) { viewerPageMap = new HashMap<Long, ViewerPage>(); List<ViewerPage> list = getPs().getViewerPages(); for(ViewerPage page : list){ viewerPageMap.put(page.getId(), page); } } } } }