package net.ion.craken.node.crud.store;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.transaction.Transaction;
import net.ion.craken.loaders.EntryKey;
import net.ion.craken.node.IndexWriteConfig;
import net.ion.craken.node.IndexWriteConfig.FieldIndex;
import net.ion.craken.node.Workspace;
import net.ion.craken.node.crud.Craken;
import net.ion.craken.node.crud.impl.SifsWorkspace;
import net.ion.craken.node.crud.tree.TreeCache;
import net.ion.craken.node.crud.tree.impl.PropertyId;
import net.ion.craken.node.crud.tree.impl.PropertyId.PType;
import net.ion.craken.node.crud.tree.impl.PropertyValue;
import net.ion.craken.node.crud.tree.impl.PropertyValue.VType;
import net.ion.craken.node.crud.tree.impl.TreeNodeKey;
import net.ion.craken.node.crud.tree.impl.TreeNodeKey.Action;
import net.ion.framework.parse.gson.JsonArray;
import net.ion.framework.parse.gson.JsonElement;
import net.ion.framework.util.ListUtil;
import net.ion.framework.util.StringUtil;
import net.ion.framework.util.WithinThreadExecutor;
import net.ion.nsearcher.common.WriteDocument;
import net.ion.nsearcher.config.Central;
import net.ion.nsearcher.config.CentralConfig;
import net.ion.nsearcher.index.IndexJob;
import net.ion.nsearcher.index.IndexSession;
import org.apache.lucene.store.Directory;
import org.infinispan.AdvancedCache;
import org.infinispan.Cache;
import org.infinispan.atomic.AtomicMap;
import org.infinispan.commands.tx.CommitCommand;
import org.infinispan.commands.write.DataWriteCommand;
import org.infinispan.commands.write.PutKeyValueCommand;
import org.infinispan.commands.write.RemoveCommand;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.ClusteringConfigurationBuilder;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.context.impl.TxInvocationContext;
import org.infinispan.interceptors.base.BaseCustomInterceptor;
import org.infinispan.io.GridFile.Metadata;
import org.infinispan.io.GridFilesystem;
import org.infinispan.lucene.directory.BuildContext;
import org.infinispan.lucene.directory.DirectoryBuilder;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.persistence.sifs.configuration.SoftIndexFileStoreConfigurationBuilder;
import org.infinispan.transaction.TransactionMode;
public class SifsFileConfigBuilder extends WorkspaceConfigBuilder {
private String rootPath;
private Central central;
private GridFilesystem gfs;
public SifsFileConfigBuilder(String rootPath) {
this.rootPath = rootPath;
}
@Override
public WorkspaceConfigBuilder build(DefaultCacheManager dm, String wsName) throws IOException {
if (StringUtil.isBlank(rootPath)) {
ClusteringConfigurationBuilder real_configBuilder = new ConfigurationBuilder().read(dm.getDefaultCacheConfiguration()).transaction().transactionMode(TransactionMode.TRANSACTIONAL).invocationBatching().enable().clustering();
ClusteringConfigurationBuilder idx_meta_builder = new ConfigurationBuilder().persistence().passivation(false).clustering().stateTransfer().timeout(300, TimeUnit.SECONDS).clustering();
ClusteringConfigurationBuilder idx_chunk_builder = new ConfigurationBuilder().persistence().passivation(false).clustering().stateTransfer().timeout(300, TimeUnit.SECONDS).clustering();
;
ClusteringConfigurationBuilder idx_lock_builder = new ConfigurationBuilder().persistence().passivation(true).clustering().stateTransfer().timeout(300, TimeUnit.SECONDS).clustering();
;
if (cacheMode().isClustered()) {
real_configBuilder.cacheMode(CacheMode.REPL_ASYNC);
idx_meta_builder.cacheMode(CacheMode.REPL_SYNC);
idx_chunk_builder.cacheMode(CacheMode.DIST_SYNC);
idx_lock_builder.cacheMode(CacheMode.REPL_SYNC);
}
dm.defineConfiguration(wsName, real_configBuilder.build());
dm.defineConfiguration(wsName + "-meta", idx_meta_builder.build());
dm.defineConfiguration(wsName + "-chunk", idx_chunk_builder.build());
dm.defineConfiguration(wsName + "-lock", idx_lock_builder.build());
this.central = makeCentral(dm, wsName);
this.gfs = makeGridSystem(dm, wsName);
} else if (StringUtil.isNotBlank(rootPath)) {
File rootFile = new File(rootPath);
String dataIndexPath = new File(rootFile, wsName + "_dataindex").getCanonicalPath();
String dataChunkPath = new File(rootFile, wsName + "_datachunk").getCanonicalPath();
String searchIndexPath = new File(rootFile, wsName + "_searchindex").getCanonicalPath();
String searchChunkPath = new File(rootFile, wsName + "_searchchunk").getCanonicalPath();
String blobIndexPath = new File(rootFile, wsName + "_blobindex").getCanonicalPath();
String blobChunkPath = new File(rootFile, wsName + "_blobchunk").getCanonicalPath();
ClusteringConfigurationBuilder real_configBuilder = null;
ClusteringConfigurationBuilder index_metaBuilder = null;
ClusteringConfigurationBuilder index_chunkBuilder = null;
ClusteringConfigurationBuilder blob_metaBuilder = null;
ClusteringConfigurationBuilder blob_chunkBuilder = null;
real_configBuilder = new ConfigurationBuilder().persistence().passivation(false).addStore(SoftIndexFileStoreConfigurationBuilder.class).fetchPersistentState(true).preload(true).shared(false).purgeOnStartup(false).ignoreModifications(false).indexLocation(dataIndexPath)
.dataLocation(dataChunkPath).async().disable().flushLockTimeout(300000).shutdownTimeout(2000).eviction().maxEntries(this.maxEntry()) // alert : no expire
.transaction().invocationBatching().enable().clustering();
index_metaBuilder = new ConfigurationBuilder().persistence().passivation(false).addSingleFileStore().fetchPersistentState(true).preload(true).shared(false).purgeOnStartup(false).ignoreModifications(false).location(rootPath).async().disable().flushLockTimeout(300000)
.shutdownTimeout(2000).clustering();
index_chunkBuilder = new ConfigurationBuilder().persistence().passivation(false).persistence().passivation(false).addStore(SoftIndexFileStoreConfigurationBuilder.class).fetchPersistentState(false).preload(true).shared(false).purgeOnStartup(false).ignoreModifications(false)
.indexLocation(searchIndexPath).dataLocation(searchChunkPath).async().disable().eviction().maxEntries(maxSegment()).clustering();
blob_metaBuilder = new ConfigurationBuilder() // .clustering().cacheMode(CacheMode.REPL_SYNC)
.persistence().passivation(false).addSingleFileStore().fetchPersistentState(true).preload(true).shared(false).purgeOnStartup(false).ignoreModifications(false).location(rootPath).async().disable().flushLockTimeout(300000).shutdownTimeout(2000).clustering();
blob_chunkBuilder = new ConfigurationBuilder().persistence().passivation(false).addStore(SoftIndexFileStoreConfigurationBuilder.class).fetchPersistentState(false).preload(true).shared(false).purgeOnStartup(false).ignoreModifications(false).indexLocation(blobIndexPath)
.dataLocation(blobChunkPath).async().disable().eviction().maxEntries(maxEntry()).clustering();
if (cacheMode().isClustered() && cacheMode().isReplicated()) {
real_configBuilder.clustering().cacheMode(CacheMode.REPL_SYNC).persistence().addClusterLoader().remoteCallTimeout(10, TimeUnit.SECONDS).clustering(); // .l1().enable().clustering() ;
index_metaBuilder.clustering().cacheMode(CacheMode.REPL_SYNC);
index_chunkBuilder.clustering().cacheMode(CacheMode.REPL_SYNC);
blob_metaBuilder.clustering().cacheMode(CacheMode.REPL_SYNC);
blob_chunkBuilder.clustering().cacheMode(CacheMode.REPL_SYNC);
} else if (cacheMode().isClustered()) {
real_configBuilder.clustering().cacheMode(CacheMode.DIST_SYNC).persistence().addClusterLoader().remoteCallTimeout(10, TimeUnit.SECONDS).clustering(); // .l1().enable().clustering() ;
index_metaBuilder.clustering().cacheMode(CacheMode.REPL_SYNC);
index_chunkBuilder.clustering().cacheMode(CacheMode.DIST_SYNC);
blob_metaBuilder.clustering().cacheMode(CacheMode.REPL_SYNC);
blob_chunkBuilder.clustering().cacheMode(CacheMode.DIST_SYNC);
}
dm.defineConfiguration(wsName, real_configBuilder.build());
dm.defineConfiguration(wsName + "-meta", index_metaBuilder.build());
dm.defineConfiguration(wsName + "-chunk", index_chunkBuilder.build());
this.central = makeCentral(dm, wsName);
dm.defineConfiguration(blobMeta(wsName), blob_metaBuilder.build());
dm.defineConfiguration(blobChunk(wsName), blob_chunkBuilder.build());
this.gfs = makeGridSystem(dm, wsName);
}
return this;
}
public GridFilesystem gfs() {
return gfs;
}
public Central central() {
return this.central;
}
private GridFilesystem makeGridSystem(DefaultCacheManager dm, String wsName) {
Cache<String, byte[]> blobChunk = dm.getCache(blobChunk(wsName));
Cache<String, Metadata> blobMeta = dm.getCache(blobMeta(wsName));
return new GridFilesystem(blobChunk, blobMeta, 8192);
}
private Central makeCentral(DefaultCacheManager dm, String wsName) throws IOException {
Cache<TreeNodeKey, AtomicMap<PropertyId, PropertyValue>> cache = dm.getCache(wsName);
String name = cache.getName();
EmbeddedCacheManager cacheManager = cache.getCacheManager();
Cache<?, ?> metaCache = cacheManager.getCache(name + "-meta");
Cache<?, ?> dataCache = cacheManager.getCache(name + "-chunk");
Cache<?, ?> lockCache = cacheManager.getCache(name + "-lock");
BuildContext bcontext = DirectoryBuilder.newDirectoryInstance(metaCache, dataCache, lockCache, name);
// bcontext.chunkSize(1024 * 1024);
Directory directory = bcontext.create();
return CentralConfig.oldFromDir(directory).indexConfigBuilder().executorService(new WithinThreadExecutor()).build();
}
@Override
public void createInterceptor(TreeCache<PropertyId, PropertyValue> tcache, Central central, com.google.common.cache.Cache<Transaction, IndexWriteConfig> trans) {
tcache.getCache().getAdvancedCache().addInterceptor(new IndexInterceptor(gfs(), central, trans), 0);
}
@Override
public Workspace createWorkspace(Craken craken, AdvancedCache<PropertyId, PropertyValue> cache) throws IOException {
return new SifsWorkspace(craken, cache, this);
}
}
class IndexInterceptor extends BaseCustomInterceptor {
private Central central;
private com.google.common.cache.Cache<Transaction, IndexWriteConfig> trans;
public IndexInterceptor(GridFilesystem gfs, Central central, com.google.common.cache.Cache<Transaction, IndexWriteConfig> trans) {
this.central = central;
this.trans = trans;
}
@Override
public Object visitCommitCommand(final TxInvocationContext ctx, CommitCommand command) throws Throwable {
if (ctx.getTransaction() == null)
return invokeNextInterceptor(ctx, command);
final IndexWriteConfig iwconfig = trans.getIfPresent(ctx.getTransaction());
if (iwconfig == null)
return invokeNextInterceptor(ctx, command);
if (iwconfig.isIgnoreIndex()) {
trans.invalidate(ctx.getTransaction());
return invokeNextInterceptor(ctx, command);
}
IndexJob<Void> indexJob = new IndexJob<Void>() {
@Override
public Void handle(IndexSession isession) throws Exception {
List<DataWriteCommand> list = extractCommand(ctx.getModifications());
for (DataWriteCommand wcom : list) {
TreeNodeKey tkey = (TreeNodeKey) wcom.getKey();
if (tkey.getFqn().isRoot())
continue;
String pathKey = tkey.fqnString();
switch (wcom.getCommandId()) {
case PutKeyValueCommand.COMMAND_ID:
WriteDocument wdoc = isession.newDocument(pathKey);
wdoc.keyword(EntryKey.PARENT, tkey.getFqn().getParent().toString());
wdoc.number(EntryKey.LASTMODIFIED, System.currentTimeMillis());
PutKeyValueCommand pcommand = (PutKeyValueCommand) wcom;
Map<PropertyId, PropertyValue> valueMap = (Map) pcommand.getValue();
for (PropertyId pid : valueMap.keySet()) {
PropertyValue pvalue = valueMap.get(pid);
JsonArray jarray = pvalue.asJsonArray();
final String propId = pid.getString();
if (pid.type() == PType.NORMAL) {
VType vtype = pvalue.type();
for (JsonElement e : jarray.toArray()) {
if (e == null)
continue;
FieldIndex fieldIndex = iwconfig.fieldIndex(propId);
fieldIndex.index(wdoc, propId, vtype, e.isJsonObject() ? e.toString() : e.getAsString());
}
} else { // refer
for (JsonElement e : jarray.toArray()) {
if (e == null)
continue;
FieldIndex.KEYWORD.index(wdoc, '@' + propId, e.getAsString());
}
}
}
if (tkey.action() == Action.CREATE)
wdoc.insert();
else
wdoc.update();
break;
case RemoveCommand.COMMAND_ID:
isession.deleteById(pathKey);
default:
break;
}
}
return null;
}
};
if (iwconfig.isAsync())
central.newIndexer().asyncIndex(indexJob);
else
central.newIndexer().index(indexJob);
trans.invalidate(ctx.getTransaction());
return invokeNextInterceptor(ctx, command);
}
private List<DataWriteCommand> extractCommand(List list) {
List<DataWriteCommand> result = ListUtil.newList();
for (Object obj : list) {
if (obj instanceof DataWriteCommand) {
DataWriteCommand wcom = (DataWriteCommand) obj;
TreeNodeKey tkey = (TreeNodeKey) wcom.getKey();
if (tkey.getType().isStructure())
continue;
result.add(wcom);
}
}
return result;
}
}