view src/main/java/org/mpi/openmind/cache/CacheService.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.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
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.Relation;
import org.mpi.openmind.repository.bo.ViewerAttribute;
import org.mpi.openmind.repository.bo.ViewerPage;

public class CacheService extends AbstractCacheService{
	

	private static Logger logger = Logger.getLogger(CacheService.class);
	
	private Map<String, Entity> lwDefs = new HashMap<String, Entity>();
	private Map<Long, Map<Long, Attribute>> defAttrs = new HashMap<Long, Map<Long, Attribute>>();
	private Map<Long, Relation> defRels = new HashMap<Long, Relation>();
	
    private boolean defsLoaded = false;
	
	public CacheService(){
		super();
		logger.info(
				"[" + Thread.currentThread().getId() + 
				"]\tInitializing CacheService HashCode=" + 
				this.hashCode());
	}
	
    /**
     * Set up definitions.
     * 
     * @param defs
     * @param atts
     * @param rels
     */
    public void initDefinitions(List<Entity> defs, List<Attribute> atts, List<Relation> rels) {
        
        for (Entity def : defs) {
            this.lwDefs.put(def.getOwnValue(), def);
        }
        
        for (Attribute att : atts) {
            if (!this.defAttrs.containsKey(att.getSourceId())) {
                this.defAttrs.put(att.getSourceId(), new HashMap<Long, Attribute>());
            }
            this.defAttrs.get(att.getSourceId()).put(att.getId(), att);
        }

        for (Relation rel : rels) {
            this.defRels.put(rel.getId(), rel);
        }

        this.defsLoaded = true;
    }
	
	public void saveLWDefinition(Entity definition){
		synchronized (lwDefs) {
			this.lwDefs.put(definition.getOwnValue(), definition);	
		}
	}
	
	public void createLWDefinition(Entity definition){
		synchronized (lwDefs) {
			this.lwDefs.put(definition.getOwnValue(), definition);	
			this.defAttrs.put(definition.getId(), new HashMap<Long, Attribute>());
		}
	}
	
	public void saveDefAttribute(Attribute att){
		if(!this.defsLoaded){
			this.throwDefLoadingException();
			return;
		}
		synchronized (defAttrs) {
			if(!this.defAttrs.containsKey(att.getSourceId())){
				this.defAttrs.put(att.getSourceId(), new HashMap<Long, Attribute>());
			}	
			this.defAttrs.get(att.getSourceId()).put(att.getId(), att);
		}
	}
	
	public void saveDefRelation(Relation rel){
		if(!this.defsLoaded){
			this.throwDefLoadingException();
			return;
		}
		synchronized (defRels) {
			this.defRels.put(rel.getId(), rel);
		}
	}
	
	public void deleteDefRelation(Relation rel){
		if(!this.defsLoaded){
			this.throwDefLoadingException();
			return;
		}
		synchronized (defRels) {
			this.defRels.remove(rel.getId());
		}
	}
	
	public void deleteDefAttribute(Attribute att){
		if(!this.defsLoaded){
			this.throwDefLoadingException();
			return;
		}
		synchronized (defAttrs) {
			if(this.defAttrs.containsKey(att.getSourceId())){
				this.defAttrs.get(att.getSourceId()).remove(att.getId());
				
			}	
		}
	}
	
	public void deleteDefinition(Long defId){
		if(!this.defsLoaded){
			this.throwDefLoadingException();
			return;
		}
		Entity lwDef = getLWDefinitionById(defId);
		if(lwDef != null){
			
			synchronized (lwDefs) {
				this.lwDefs.remove(lwDef.getOwnValue());	
			}
			
			synchronized (defAttrs) {
				this.defAttrs.remove(defId);	
			}
			
			List<Relation> toRemove = new ArrayList<Relation>();
			for(Relation rel : this.defRels.values()){
				if(rel.getSourceId().equals(defId) || rel.getTargetId().equals(defId)){
					toRemove.add(rel);
				}
			}			
			synchronized (defRels) {
				for(Relation rel : toRemove){
					this.defRels.remove(rel.getId());
				}	
			}	
		}
	}
	
	
	
	
	public boolean areDefsLoaded(){
		return defsLoaded;
	}
	
	public int getEntitiesCount(String def){
		return this.loadEntitiesByDef(def).size();
		
	}

	
	public void updateRelationAsNode(Relation rel){
		this.updateRel(rel);
	}
	
	public Entity getEntityContent(Entity ent) throws Exception{
		if(ent.isLightweight()){	
			boolean valid = false;
			List<Attribute> atts = getAttsBySrcId(ent.getId());
			if(atts != null){
				ent.setAttributes(atts);
				Collection<Relation> srcRels = getRelsBySrcId(ent.getId());
				if(srcRels != null){
					ent.setSourceRelations(new ArrayList<Relation>(srcRels));
					Collection<Relation> tarRels = getRelsByTarId(ent.getId());
					if(tarRels != null){
						ent.setTargetRelations(new ArrayList<Relation>(tarRels));
						ent.setLightweight(false);
						valid = true;
					}
				}
			}
			if(valid){
				return ent;
			}
		}
		return null;
	}
	
	/**
	 * Returns all (lightweight) definition entities.
	 * 
	 * @return
	 */
	public List<Entity> getLWDefinitions(){
		List<Entity> list = new ArrayList<Entity>(this.lwDefs.values());
		Collections.sort(list);
		return list;
	}
	
	/**
	 * Returns the (lightweight) definition entity for the given type.
	 * 
	 * @param objectClass
	 * @return
	 */
	public Entity getLWDefinition(String objectClass){
		Entity def = this.lwDefs.get(objectClass);
		return def;
	}
	
	private void throwDefLoadingException(){
		try {
			throw new Exception("the definitions were not loaded");
		} catch (Exception e) {
			logger.error(e);		
		}		
	}
	
    /**
     * Returns the attributes of the definition of the given type.
     * 
     * @param objectClass
     * @return
     */
    public List<Attribute> getDefAttributes(String objectClass) {
        if (!this.defsLoaded) {
            this.throwDefLoadingException();
            return null;
        }
        Entity lwDef = getLWDefinition(objectClass);
        if (lwDef != null) {
            List<Attribute> list = null;
            if (this.defAttrs.containsKey(lwDef.getId())) {
                list = new ArrayList<Attribute>(this.defAttrs.get(lwDef.getId()).values());
                Collections.sort(list);
            } else {
                list = new ArrayList<Attribute>();
            }
            return list;
        }
        return new ArrayList<Attribute>();
    }
	
	public List<Attribute> getDefAttributesById(Long id){
		if(!this.defsLoaded){
			this.throwDefLoadingException();
			return null;
		}
		
		if(this.defAttrs.containsKey(id)){
			List<Attribute> list = new ArrayList<Attribute>(this.defAttrs.get(id).values());
			Collections.sort(list);
			return list;	
		}
			
		return new ArrayList<Attribute>();
	}
	
	/**
	 * Returns the source relations of the definition of the given type.
	 * 
	 * @param objectClass
	 * @return
	 */
	public List<Relation> getDefSourceRelations(String objectClass){
		if(!this.defsLoaded){
			this.throwDefLoadingException();
			return null;
		}
		Entity lwDef = getLWDefinition(objectClass);
		List<Relation> list = new ArrayList<Relation>();
		if(lwDef != null){
			for(Relation rel : this.defRels.values()){
				if(lwDef.getId().equals(rel.getSourceId())){
					list.add(rel);
				}
			}
		}
		Collections.sort(list);
		return list;
	}
	
	/**
	 * Returns the target relations of the definition of the given type.
	 * 
	 * @param objectClass
	 * @return
	 */
	public List<Relation> getDefTargetRelations(String objectClass){
		if(!this.defsLoaded){
			try {
				throw new Exception("the definitions were not loaded when getDefTargetRelations");
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		Entity lwDef = getLWDefinition(objectClass);
		List<Relation> list = new ArrayList<Relation>();
		if(lwDef != null){
			for(Relation rel : this.defRels.values()){
				if(lwDef.getId().equals(rel.getTargetId())){
					list.add(rel);
				}
			}
		}
		Collections.sort(list);
		return list;
	}
	
	public Entity getLWDefinitionById(Long id){
		for(Entity def : lwDefs.values()){
			if(def.getId().equals(id)){
				return def;
			}
		}
		return null;
	}
	
	/**
	 * Puts the entity and its Attributes and Relations in the cache.
	 * 
	 * This method should be called when a entity has been changed.
	 * It means that every trail of the old entity will be removed from the cache.
	 * @param ent
	 */
	public void saveEntity(Entity ent){
		try {
			logger.debug("Saving in cache " + ent.getObjectClass() + " - " + ent.getId());
			this.deleteEntity(ent.getId(), ent.getObjectClass());
			
			this.updateLWEntity(ent);
			
			if(!ent.isLightweight()){
				this.updateRelList(ent.getSourceRelations());
				this.updateRelList(ent.getTargetRelations());
				this.updateAttList(ent.getAttributes());
			}	
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}
	
	/**
	 * Remove the Entity and its Attributes and Relations from the cache.
	 *  
	 * @param entId
	 * @param oc
	 */
	public void deleteEntity(Long entId, String oc){
		try {
			this.removeEntity(entId, oc);
			List<Attribute> attList = getAttsBySrcId(entId);
			for(Attribute att : attList){
				removeAtt(att);
			}
			List<Relation> srcRelList = getRelsBySrcId(entId);
			for(Relation rel : srcRelList){
				this.removeRel(rel);
			}
			
			List<Relation> tarRelList = getRelsByTarId(entId);
			for(Relation rel : tarRelList){
				this.removeRel(rel);
			}
			
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}
	

	/**
	 * Returns if this Entity and its Attributes and Relations are up-to-date with the cache.
	 * 
	 * Checks accessTimes only.
	 * 
	 * @param ent
	 * @return
	 */
	public boolean isEntityCurrent(Entity ent) {
		// check entity
		if (!isLWEntityCurrent(ent)) {
			return false;
		}
		
		if (ent.isLightweight()) {
			logger.error("Can not check attributes and relations of lightweight entity!");
			return false;
		}
		Long entId = ent.getId();
		
		// check attached attributes
		List<Attribute> entAtts = ent.getAttributes();
		List<Attribute> cacheAtts = attMap.getValuesByBKey(entId);
		for (Attribute att : entAtts) {
			if (!isAttCurrent(att)) {
				return false;
			} else {
				if (!cacheAtts.remove(att)) {
					return false;
				}
			}
		}		
		// check additional attributes in cache
		if (!cacheAtts.isEmpty()) {
			for (Attribute ca : cacheAtts) {
				// ignore empty attributes
				if (!StringUtils.isEmpty(ca.getOwnValue())) {
					return false;
				}
			}
		}
		
		// check attached source relations
		List<Relation> entSrcRels = ent.getSourceRelations();
		List<Relation> cacheSrcRels = relMap.getValuesByBKey(entId);
		for (Relation rel: entSrcRels) {
			if (!isRelCurrent(rel)) {
				return false;
			} else {
				if (!cacheSrcRels.remove(rel)) {
					return false;
				}
			}
		}
		if (!cacheSrcRels.isEmpty()) {
			return false;
		}
		// check attached target relations
		List<Relation> entTarRels = ent.getTargetRelations();
		List<Relation> cacheTarRels = relMap.getValuesByCKey(entId);
		for (Relation rel: entTarRels) {
			if (!isRelCurrent(rel)) {
				return false;
			} else {
				if (!cacheTarRels.remove(rel)) {
					return false;
				}
			}
		}
		if (!cacheTarRels.isEmpty()) {
			return false;
		}	
		
		return true;
	}

	
	public class EntityDiff {
		public List<Attribute> addedAttributes = new ArrayList<Attribute>();
		public List<Attribute> removedAttributes = new ArrayList<Attribute>();
		public List<Attribute> modifiedAttributes = new ArrayList<Attribute>();
		public List<Relation> addedSrcRels = new ArrayList<Relation>();
		public List<Relation> removedSrcRels = new ArrayList<Relation>();
		public List<Relation> modifiedSrcRels = new ArrayList<Relation>();
		public List<Relation> addedTarRels = new ArrayList<Relation>();
		public List<Relation> removedTarRels = new ArrayList<Relation>();
		public List<Relation> modifiedTarRels = new ArrayList<Relation>();
	}

	/**
	 * Returns an EntityDiff with the differences between the given Entity and the current cache.
	 * 
	 * Ignores empty Attributes. Compares only content if acceptNewIDs.
	 * 
	 * @param ent
	 * @return
	 * @throws Exception
	 */
	public EntityDiff diffEntityCache(Entity ent, boolean acceptNewIDs) throws Exception {
		if (ent.isLightweight()) {
			throw new IllegalStateException("Can not diff attributes and relations of lightweight entity!");
		}
		Long entId = ent.getId();
		EntityDiff diff = new EntityDiff();
		
		/*
		 * check attributes
		 */
		List<Attribute> entAtts = ent.getAttributes();
		List<Attribute> cacheAtts = attMap.getValuesByBKey(entId);
		// check attached attributes and compare to cached
		for (Attribute att : entAtts) {
			if (!isAttCurrent(att)) {
				diff.modifiedAttributes.add(att);
			}
			if (!cacheAtts.remove(att)) {
				boolean found = false;
				for (Attribute ca : cacheAtts) {
					if (ca.getId() == att.getId()) {
						cacheAtts.remove(ca);
						found = true;
						break;
					} else if (acceptNewIDs && ca.equalsContent(att)) {
						// same content is good enough
						cacheAtts.remove(ca);
						found = true;
						break;
					}
				}
				if (!found) {
					diff.addedAttributes.add(att);
				}
			}
		}
		if (!cacheAtts.isEmpty()) {
			// more cached attributes
			for (Attribute ca : cacheAtts) {
				// ignore empty attributes
				if (!StringUtils.isEmpty(ca.getOwnValue())) {
					diff.removedAttributes.add(ca);
				}
			}
		}
		
		/*
		 * source relations
		 */
		List<Relation> entSrcRels = ent.getSourceRelations();
		List<Relation> cacheSrcRels = relMap.getValuesByBKey(entId);
		// check attached source relations and compare to cached
		for (Relation rel : entSrcRels) {
			if (!isRelCurrent(rel)) {
				diff.modifiedSrcRels.add(rel);
			}
			if (!cacheSrcRels.remove(rel)) {
				// rel was not in cache
				boolean found = false;
				for (Relation cr : cacheSrcRels) {
					// check all cached relations
					if (cr.getId() == rel.getId()) {
						// relation id is same
						cacheSrcRels.remove(cr);
						found = true;
						break;
					} else if (acceptNewIDs && cr.equalsContent(rel)) {
						// same content is good enough 
						// (includes checks for src and tar id)
						cacheSrcRels.remove(cr);
						found = true;
						break;
					}
				}
				if (!found) {
					diff.addedSrcRels.add(rel);
				}
			}
		}
		if (!cacheSrcRels.isEmpty()) {
			for (Relation rel : cacheSrcRels) {
				diff.removedSrcRels.add(rel);
			}
		}
		
		/*
		 * target relations
		 */
		List<Relation> entTarRels = ent.getTargetRelations();
		List<Relation> cacheTarRels = relMap.getValuesByCKey(entId);
		// check attached target relations and compare to cached
		for (Relation rel : entTarRels) {
			if (!isRelCurrent(rel)) {
				diff.modifiedTarRels.add(rel);
			}
			if (!cacheTarRels.remove(rel)) {
				// rel was not in cache
				boolean found = false;
				for (Relation cr : cacheTarRels) {
					// check all cached relations
					if (cr.getId() == rel.getId()) {
						// relation id is same
						cacheTarRels.remove(cr);
						found = true;
						break;
					} else if (acceptNewIDs && cr.equalsContent(rel)) {
						// same content is good enough 
						// (includes checks for src and tar id)
						cacheTarRels.remove(cr);
						found = true;
						break;
					}
				}
				if (!found) {
					diff.addedTarRels.add(rel);
				}
			}
		}
		if (!cacheTarRels.isEmpty()) {
			for (Relation rel : cacheTarRels) {
				diff.removedTarRels.add(rel);
			}
		}
		
		// return null if diff is empty
		if (diff.addedAttributes.isEmpty() && diff.removedAttributes.isEmpty() && diff.modifiedAttributes.isEmpty()
				&& diff.addedSrcRels.isEmpty() && diff.removedSrcRels.isEmpty() && diff.modifiedSrcRels.isEmpty()
				&& diff.addedTarRels.isEmpty() && diff.removedTarRels.isEmpty() && diff.modifiedTarRels.isEmpty()) {
			return null;
		}
		return diff;
	}
	

	
	
	
	public List<ViewerAttribute> getViewerAttributes(Long page){
		return getViewerAttMap().getValuesByAKey(page);
	}
	
	public Collection<ViewerPage> getViewerPages(){
		return getViewerPageMap().values();
	}
	
	public ViewerPage saveViewerPage(ViewerPage page){
		this.getPs().saveViewerPage(page);
		this.getViewerPageMap().put(page.getId(), page);
		return page;
	}
	
	public ViewerAttribute saveViewerAttribute(ViewerPage page, ViewerAttribute att) throws Exception{
		logger.info("saveViewerAttribute Page[id="+ page.getId() +", label=" + page.getLabel() + "] Att[id="+ att.getId() +", label="+ att.getLabel() +"]");
		if(!page.isPersistent()){
			throw new Exception("The ViewerPage associated the the current attribute is not persistent.");
		}
		att.setPage(page.getId());
		this.getPs().saveViewerAttribute(att);
		this.getViewerAttMap().put(att.getKey(), att);
		return att;
	}
	
	
}