All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.logicbus.kvalue.cache2.KValueCacheStore Maven / Gradle / Ivy

package com.logicbus.kvalue.cache2;

import com.alogic.cache.CacheObject;
import com.alogic.load.Loader;
import com.alogic.load.Store;
import com.alogic.xscript.Logiclet;
import com.alogic.xscript.LogicletContext;
import com.alogic.xscript.Script;
import com.alogic.xscript.doc.XsObject;
import com.alogic.xscript.doc.json.JsonObject;
import com.anysoft.util.*;
import com.logicbus.kvalue.context.KValueSource;
import com.logicbus.kvalue.core.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.util.HashMap;
import java.util.List;

/**
 * 基于KValue的缓存Store实现
 * 
 * @author yyduan
 *
 * @since 1.6.14.9 [20210507 duanyy]
 */
public class KValueCacheStore extends Loader.Sinkable implements Store{

	/**
	 * 用于保存Hash类数据的表
	 */
	protected Table hashTable;

	/**
	 * 用于保存id的表
	 */
	protected Table idTable;
	
	/**
	 * 本缓存的id
	 */
	protected String id;
	
	/**
	 * 是否在idTable中保存id列表
	 */
	protected boolean enableIdTable = false;
	
	/**
	 * 加载事件脚本
	 */
	protected Logiclet onLoad = null;

	/**
	 * 保存事件脚本
	 */
	protected Logiclet onSave = null;

	protected Logiclet onDel = null;
	
	protected String cacheObjectId = "$cache-object";

	protected String objectId = "$cache-object-id";
	
	protected boolean ttlUpdate = true;
	
	/**
	 * 获取id
	 * @return id
	 */
	public String getId(){
		return id;
	}
	
	@Override
	public void configure(Properties p){
		super.configure(p);
		
		cacheObjectId = PropertiesConstants.getString(p,"cacheObjectId",cacheObjectId,true);
		objectId = PropertiesConstants.getString(p,"objectId",objectId,true);
		
		id = PropertiesConstants.getString(p, "id", "",true);
		enableIdTable = PropertiesConstants.getBoolean(p, "table.id.enable", enableIdTable);
		ttlUpdate = PropertiesConstants.getBoolean(p, "ttl.update", ttlUpdate);
		String schema = PropertiesConstants.getString(p,"schema","redis");
		String hashTableName = PropertiesConstants.getString(p,"table.hash","m");
		String idtableName = PropertiesConstants.getString(p,"table.id","i");
		Schema instance = KValueSource.getSchema(schema);
		if (instance == null){
			throw new BaseException("core.e1003","Can not find a kvalue schema named " + schema);
		}
		
		hashTable = instance.getTable(hashTableName);
		if (hashTable == null){
			throw new BaseException("core.e1003","Can not find a kvalue table named " + hashTableName);
		}

		idTable = instance.getTable(idtableName);
		if (idTable == null){
			throw new BaseException("core.e1003","Can not find a kavalue table named " + idtableName);
		}
	}
	
	@Override
	public void configure(Element e, Properties p) {
		Properties props = new XmlElementProperties(e,p);
		configure(props);
		
		NodeList nodeList = XmlTools.getNodeListByPath(e, getSinkTag());
		Factory> factory = new Factory>();
		String scope = PropertiesConstants.getString(p, "ketty.scope", "runtime");
		
		for (int i = 0 ;i < nodeList.getLength() ; i ++){
			Node n = nodeList.item(i);
			
			if (Node.ELEMENT_NODE != n.getNodeType()){
				continue;
			}
			
			Element elem = (Element)n;

			XmlElementProperties itemProps = new XmlElementProperties(elem,props);
			String itemScope = PropertiesConstants.getString(itemProps,"scope","",true);
			if (StringUtils.isNotEmpty(itemScope) && !itemScope.equals(scope)){
				continue;
			}

			boolean enable = PropertiesConstants.getBoolean(itemProps,"enable",true,true);
			if (!enable){
				continue;
			}
			
			try {
				Loader loader = factory.newInstance(elem, props, "module");
				if (loader != null){
					loaders.add(loader);
				}
			}catch (Exception ex){
				LOG.error("Can not create loader from element:" + XmlTools.node2String(elem));
				LOG.error(ExceptionUtils.getStackTrace(ex));
			}
		}
		
		Element onLoadElem = XmlTools.getFirstElementByPath(e, "on-load");
		if (onLoadElem != null){
			onLoad = Script.create(onLoadElem, props);
		}
		Element onSaveElem = XmlTools.getFirstElementByPath(e, "on-save");
		if (onSaveElem != null){
			onSave = Script.create(onSaveElem, props);
		}
		Element onDelElem = XmlTools.getFirstElementByPath(e, "on-expire");
		if (onDelElem != null){
			onDel = Script.create(onDelElem, props);
		}
	}	
	
	/**
	 * 根据对象id生成在缓存中的id
	 * @param id 对象id
	 * @return 缓存中的id
	 */
	protected String getRowId(String id){
		return this.getId() + '$' + id;
	}
	
	/**
	 * 根据id生成一个缓存对象(该对象不一定存在数据)
	 * @param id 对象id
	 * @return 缓存对象
	 */
	protected CacheObject getCacheObject(String id){
		String rowId = getRowId(id);
		HashRow hash = (HashRow) hashTable.select(rowId, true);
		
		long ttl = this.getTTL();
		if (ttl <= 0){
			ttl = 30 * 60 * 1000L;
		}
		return new KValueCacheObject(id,hash,ttl,ttlUpdate);
	}
	
	@Override
	public CacheObject load(String id, boolean cacheAllowed) {
		if (noCache()){
			return loadFromSink(id,cacheAllowed);
		}else{
			CacheObject found = loadFromSelf(id,cacheAllowed);
			if (found == null){
				if (hasSink()) {
					synchronized (this) {
						found = loadFromSelf(id, cacheAllowed);
						if (found == null) {
							found = loadFromSink(id, cacheAllowed);
							if (found != null) {
								onLoad(id, found);
								cacheSave(id, found, true);
							}
						}
					}
				}
			}		
			return found;
		}
	}

	protected void onSave(String id, CacheObject cache) {
		if (onSave != null){
			LogicletContext logicletContext = new LogicletContext(Settings.get());

			try {
				logicletContext.setObject(cacheObjectId, cache);
				XsObject doc = new JsonObject("root",new HashMap());
				onSave.execute(doc,doc, logicletContext, null);
			}catch (Exception ex){
				LOG.info("Failed to execute onload script" + ExceptionUtils.getStackTrace(ex));
			}finally{
				logicletContext.removeObject(cacheObjectId);
			}
		}
	}

	protected void onLoad(String id, CacheObject cache) {
		if (onLoad != null){
			LogicletContext logicletContext = new LogicletContext(Settings.get());
	
			try {
				logicletContext.setObject(cacheObjectId, cache);
				XsObject doc = new JsonObject("root",new HashMap());
				onLoad.execute(doc,doc, logicletContext, null);
			}catch (Exception ex){
				LOG.info("Failed to execute onload script" + ExceptionUtils.getStackTrace(ex));
			}finally{
				logicletContext.removeObject(cacheObjectId);
			}
		}
	}

	protected void onDel(String id) {
		if (onDel != null){
			LogicletContext logicletContext = new LogicletContext(Settings.get());
			try {
				logicletContext.SetValue(objectId,id);
				XsObject doc = new JsonObject("root",new HashMap());
				onDel.execute(doc,doc, logicletContext, null);
			}catch (Exception ex){
				LOG.info("Failed to execute onDel script" + ExceptionUtils.getStackTrace(ex));
			}
		}
	}

	@Override
	public CacheObject newObject(String id) {
		return getCacheObject(id);
	}

	@Override
	public void save(String id, CacheObject o, boolean overwrite) {
		if (!noCache()) {
			cacheSave(id, o, overwrite);
		}
		onSave(id,o);
	}

	public void cacheSave(String id, CacheObject o, boolean overwrite) {
		if (o != null){
			CacheObject kvObject = getCacheObject(id);
			o.copyTo(kvObject);
			
			if (enableIdTable){
				SortedSetRow idRow = (SortedSetRow)idTable.select(getId(),true);
				idRow.add(id, System.currentTimeMillis());
			}
		}
	}

	@Override
	public void del(String id) {
		CacheObject kvObject = getCacheObject(id);
		if (kvObject.isValid()){
			kvObject.expire();
			onDel(id);
		}
	}

	@Override
	public void scan(List result, Pager pager) {
		SortedSetRow idRow = (SortedSetRow)idTable.select(getId(),true);
		long max = System.currentTimeMillis();
		long min = max - getTTL();
		List ids = idRow.rangeByScore(min, max, true, pager.getOffset(), pager.getLimit());
		long all = idRow.count(min, max);
		
		String keyword = pager.getKeyword();
		int offset = pager.getOffset();
		int limit = pager.getLimit();
	
		int current = 0;
		for (String id:ids){
			boolean match = StringUtils.isEmpty(pager.getKeyword()) || id.contains(keyword);
			if (match){
				if (current >= offset && current < offset + limit){
					result.add(id);
				}
				current ++;
			}
		}
		
		pager.setAll(all).setTotal(current);
	}

	@Override
	protected CacheObject loadFromSelf(String id, boolean cacheAllowed) {
		if (cacheAllowed) {
			CacheObject kvObject = getCacheObject(id);
			if (kvObject.isValid()) {
				if (enableIdTable) {
					SortedSetRow idRow = (SortedSetRow) idTable.select(getId(), true);
					idRow.add(id, System.currentTimeMillis());
				}
				return kvObject;
			}
		}
		return null;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy