package net.ion.craken.loaders; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.LinkedHashSet; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; import net.ion.craken.EntryKey; import net.ion.framework.logging.LogBroker; import net.ion.framework.parse.gson.JsonParser; import net.ion.neo.NodeCursor; import net.ion.neo.ReadNode; import net.ion.neo.ReadRelationship; import net.ion.neo.ReadSession; import net.ion.neo.TransactionJob; import net.ion.neo.WriteNode; import net.ion.neo.WriteRelationship; import net.ion.neo.WriteSession; import org.infinispan.Cache; import org.infinispan.container.entries.ImmortalCacheValue; import org.infinispan.container.entries.InternalCacheEntry; import org.infinispan.loaders.AbstractCacheStore; import org.infinispan.loaders.CacheLoaderConfig; import org.infinispan.loaders.CacheLoaderException; import org.infinispan.loaders.CacheLoaderMetadata; import org.infinispan.marshall.StreamingMarshaller; @CacheLoaderMetadata(configurationClass = NeoNodeCacheStoreConfig.class) public class NeoNodeCacheStore extends AbstractCacheStore { private static final Logger log = LogBroker.getLogger(NeoNodeCacheStore.class); private NeoNodeCacheStoreConfig config; private ReadSession session; private static final String ValueFieldName = "__value"; private static final String IDProp = "_id"; private static final String JSONProp = "_json"; @Override public void init(CacheLoaderConfig config, Cache<?, ?> cache, StreamingMarshaller m) throws CacheLoaderException { super.init(config, cache, m); this.config = (NeoNodeCacheStoreConfig) config; } @Override public void start() throws CacheLoaderException { super.start(); this.session = config.login(); } @Override public void store(final InternalCacheEntry entry) throws CacheLoaderException { try { Boolean result = session.tran(new TransactionJob<Boolean>() { @Override public Boolean handle(WriteSession wsession) { try { WriteNode found = wsession.createQuery().parseQuery(IDProp + ":" + entry.getKey()).findOne(); if (found == null) found = wsession.newNode().property(IDProp, entry.getKey()); if (entry.canExpire()) { long expiry = entry.getExpiryTime(); if (entry.getMaxIdle() > 0) { // Coding getExpiryTime() for transient entries has the risk of being a moving target // which could lead to unexpected results, hence, InternalCacheEntry calls are required expiry = entry.getMaxIdle() + System.currentTimeMillis(); } found.property("expiry", expiry); } found.property(IDProp, entry.getKey()); found.property(JSONProp, JsonParser.fromObject(entry.getValue()).toString()) ; found.property(ValueFieldName, marshaller.objectToByteBuffer(entry)); return Boolean.TRUE; } catch (InterruptedException ex) { ex.printStackTrace(); return Boolean.FALSE; } catch (IOException ex) { ex.printStackTrace(); return Boolean.FALSE; } } }).get(); } catch (InterruptedException e) { throw new CacheLoaderException(e) ; } catch (ExecutionException e) { throw new CacheLoaderException(e) ; } } @Override public InternalCacheEntry load(Object key) throws CacheLoaderException { // session.createQuery().find().toList() ; ReadNode read = session.createQuery().parseQuery(IDProp + ":" + transKey(key)).findOne(); if (read == null) { return null; } InternalCacheEntry entry = toCacheEntry(read) ; if (entry != null && entry.isExpired(System.currentTimeMillis())) { return null; } return entry; } private Object transKey(Object key) { return (key instanceof EntryKey) ? ((EntryKey) key).get() : key; } @Override public Set<InternalCacheEntry> loadAll() throws CacheLoaderException { Set<InternalCacheEntry> set = new LinkedHashSet<InternalCacheEntry>(); NodeCursor<ReadNode, ReadRelationship> cursor = session.createQuery().find(); while (cursor.hasNext()) { ReadNode raw = cursor.next(); set.add(toCacheEntry(raw)) ; } return set; } @Override public Set<InternalCacheEntry> load(int numEntries) throws CacheLoaderException { Set<InternalCacheEntry> set = new LinkedHashSet<InternalCacheEntry>(); NodeCursor<ReadNode, ReadRelationship> cursor = session.createQuery().atLength(numEntries).find(); while (cursor.hasNext()) { ReadNode raw = cursor.next(); set.add(toCacheEntry(raw)) ; } return set; } private InternalCacheEntry toCacheEntry(ReadNode raw){ // byte[] bytes = (byte[]) raw.property(ValueFieldName); // Object readObject = marshaller.objectFromByteBuffer(bytes); return new ImmortalCacheValue(Employee.createEmp(20, "incache", 99)).toInternalCacheEntry(raw.property(IDProp)); } @Override protected void purgeInternal() throws CacheLoaderException { session.tran(new TransactionJob<Integer>() { @Override public Integer handle(WriteSession wsession) { NodeCursor<WriteNode, WriteRelationship> cursor = wsession.createQuery().parseQuery("expiry:[0 TO " + System.currentTimeMillis() + "]").find(); int count = 0; while (cursor.hasNext()) { cursor.next().remove(); count++; } return count; } }); } @Override public Set<Object> loadAllKeys(Set<Object> keysToExclude) throws CacheLoaderException { NodeCursor<ReadNode, ReadRelationship> nc = session.createQuery().find(); Set<Object> keySet = new LinkedHashSet<Object>(); while(nc.hasNext()){ ReadNode node = nc.next(); if (! keysToExclude.contains(node.property(IDProp))) keySet.add(node) ; } return keySet ; } @Override public void clear() throws CacheLoaderException { try { session.dropWorkspace(); } catch (IOException e) { throw new CacheLoaderException(e); } } @Override public boolean remove(Object key) throws CacheLoaderException { Future<Boolean> future = session.tran(new TransactionJob<Boolean>() { @Override public Boolean handle(WriteSession wsession) { WriteNode node = wsession.createQuery().findOne(); if (node == null) return false; else node.remove(); return true; } }); log.log(Level.INFO, "removed %s expired records", future); return false; } @Override public void fromStream(ObjectInput inputStream) throws CacheLoaderException { new UnsupportedOperationException("fromStream").printStackTrace(); } @Override public void toStream(ObjectOutput outputStream) throws CacheLoaderException { new UnsupportedOperationException("toStream").printStackTrace(); } @Override public Class<? extends CacheLoaderConfig> getConfigurationClass() { return NeoNodeCacheStoreConfig.class ; } public ReadSession session(){ return session ; } }