view src/main/java/org/mpi/openmind/cache/AbstractCacheService.java @ 41:2079c35739c8

more comments and cleanup.
author casties
date Mon, 17 Oct 2016 12:28:39 +0200
parents 86c343109257
children b7a8db041f68
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;
	
	private 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){
		this.entMap.put(ent.getKey(), 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.
	 * 
	 * Do not modify the entity!
	 * 
	 * @param id
	 * @return
	 */
	public Entity getEntityByIdReadOnly(Long id){
		if (id == null) {
		    // TODO: WTF?
			try {
				throw new Exception("Id of entity can not be null.");
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
		} 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());
		        }
			}	
			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() + "]\tgetEntitiesByDef=" + oc + "\nHashCode=" + 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() + "]\t Starting loading Entities from DB for OC="
                            + oc + "\nHashCode=" + 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) {
                        entMap.put(ent.getKey(), ent);
                    }
                    
                    entLoadedByOC.put(oc, true);
                    long end = System.currentTimeMillis();
                    logger.debug("[" + Thread.currentThread().getId() + "]\tFinished loading Entities from DB for OC="
                            + oc + " - time= " + (end - start) + "\nHashCode=" + 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){
			entMap.put(ent.getKey(), ent);
		}
	}
	
	//******************************************
	//************Attributes********************
	//******************************************
	
	//AttKey(OC, attName), SrcId
	private 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){
		this.attMap.put(att.getKey(), att);
	}
	
	/**
	 * Put list of attributes in cache.
	 * 
	 * @param list
	 */
	protected void updateAttList(List<Attribute> list){
		for(Attribute att : list){
			this.attMap.put(att.getKey(), att);	
		}
	}
	
	/**
	 * Return a list of (cloned) Attributes of the Entity.
	 * 
	 * 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;
	}
	
	public List<Attribute> getAttsBySrcIdReadOnly(Long srcId){
		//logger.info("getAttsBySrcIdReadOnly srcId=" + srcId);
		if(attLoadedBySrcId.get(srcId) == null || attLoadedBySrcId.get(srcId) == false){
			synchronized (attMap) {
				if(attLoadedBySrcId.get(srcId) == null){
					//logger.info("getAttsBySrcIdReadOnly fromDB entId=" + srcId);
					attLoadedBySrcId.put(srcId, false);
					List<Attribute> list = 
						getPs().getAllAttributes(srcId, -1);
					for(Attribute att : list){
						this.attMap.put(att.getKey(), att);
					}
					attLoadedBySrcId.put(srcId, true);
					//logger.info("STARTING getAttsBySrcIdReadOnly srcId=" + srcId + " - size=" + list.size());
				}
			}
		}
		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){
						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);
	}
	
	//******************************************
	//************Relations*********************
	//******************************************
	//RelKey<>, src, tar, relId
	private 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){
		this.relMap.put(rel.getKey(), rel);
	}
	
	/**
	 * Put list of Relations in cache.
	 * 
	 * @param list
	 */
	protected void updateRelList(List<Relation> list){
		for(Relation rel : list){
			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) {
                        this.relMap.put(rel.getKey(), rel);
                    }
                    relLoadedBySrcId.put(srcId, true);
                }
            }
        }
        return relMap.getValuesByBKey(srcId);
    }
	
	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){
						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){
						this.relMap.put(rel.getKey(), rel);
					}
					
					relLoadedByName.put(name, true);
				}
			}
		}
		
		return relMap.getValuesByDKey(name);
	}
	
	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);
				}
			}
		}
	}
}