package com.bagri.server.hazelcast.impl;
import static com.bagri.core.Constants.ctx_repo;
import static com.bagri.core.Constants.ctx_popService;
import static com.bagri.core.Constants.pn_schema_format_default;
import static com.bagri.core.server.api.CacheConstants.*;
import static com.bagri.server.hazelcast.util.HazelcastUtils.hasStorageMembers;
import static com.bagri.server.hazelcast.util.HazelcastUtils.findSystemInstance;
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 java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import com.bagri.core.KeyFactory;
import com.bagri.core.api.BindingManagement;
import com.bagri.core.api.TransactionManagement;
import com.bagri.core.api.BagriException;
import com.bagri.core.api.impl.SchemaRepositoryBase;
import com.bagri.core.model.Path;
import com.bagri.core.server.api.ClientManagement;
import com.bagri.core.server.api.ContentBuilder;
import com.bagri.core.server.api.ContentHandler;
import com.bagri.core.server.api.ContentModeler;
import com.bagri.core.server.api.ContentParser;
import com.bagri.core.server.api.IndexManagement;
import com.bagri.core.server.api.ModelManagement;
import com.bagri.core.server.api.PopulationManagement;
import com.bagri.core.server.api.SchemaRepository;
import com.bagri.core.server.api.TriggerManagement;
import com.bagri.core.server.api.df.map.MapHandler;
import com.bagri.core.server.api.df.json.JsonpHandler;
import com.bagri.core.server.api.df.xml.XmlHandler;
import com.bagri.core.system.DataFormat;
import com.bagri.core.system.Index;
import com.bagri.core.system.Library;
import com.bagri.core.system.Module;
import com.bagri.core.system.Schema;
import com.bagri.core.system.TriggerDefinition;
import com.bagri.core.xquery.api.XQProcessor;
import com.hazelcast.core.HazelcastInstance;
public class SchemaRepositoryImpl extends SchemaRepositoryBase implements ApplicationContextAware, SchemaRepository {
private static final transient Logger logger = LoggerFactory.getLogger(SchemaRepositoryImpl.class);
private ThreadLocal<String> thClient = new ThreadLocal<String>() {
@Override
protected String initialValue() {
return null;
}
};
private KeyFactory xdmFactory;
//private String instanceNum;
private Schema xdmSchema;
private Map<String, DataFormat> xdmFormats;
private Collection<Module> xdmModules;
private Collection<Library> xdmLibraries;
private ClientManagement clientMgr;
private IndexManagement indexMgr;
private ModelManagement modelMgr;
private PopulationManagement popMgr;
private TriggerManagement triggerMgr;
private ApplicationContext appContext;
private HazelcastInstance hzInstance;
private Map<String, XQProcessor> processors = new ConcurrentHashMap<String, XQProcessor>();
private ConcurrentHashMap<String, ContentHandler> handlers = new ConcurrentHashMap<String, ContentHandler>();
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.appContext = context;
}
public HazelcastInstance getHzInstance() {
return hzInstance;
}
//@Autowired
public void setHzInstance(HazelcastInstance hzInstance) {
this.hzInstance = hzInstance;
hzInstance.getUserContext().put(ctx_repo, this);
setPopulationManagement((PopulationManagement) hzInstance.getUserContext().get(ctx_popService));
logger.debug("setHzInstange; got instance: {}", hzInstance.getName());
}
@Override
public void setBindingManagement(BindingManagement bindMgr) {
super.setBindingManagement(bindMgr);
((BindingManagementImpl) bindMgr).setRepository(this);
}
@Override
public ClientManagement getClientManagement() {
return clientMgr;
}
public void setClientManagement(ClientManagement clientMgr) {
this.clientMgr = clientMgr;
((ClientManagementImpl) clientMgr).setRepository(this);
}
@Override
public IndexManagement getIndexManagement() {
return indexMgr;
}
public void setIndexManagement(IndexManagement indexMgr) {
this.indexMgr = indexMgr;
((IndexManagementImpl) indexMgr).setRepository(this);
}
public ModelManagement getModelManagement() {
return modelMgr;
}
@Override
public PopulationManagement getPopulationManagement() {
return popMgr;
}
public void setPopulationManagement(PopulationManagement popMgr) {
this.popMgr = popMgr;
}
public void setModelManagement(ModelManagement modelMgr) {
this.modelMgr = modelMgr;
}
@Override
public void setTxManagement(TransactionManagement txMgr) {
super.setTxManagement(txMgr);
((TransactionManagementImpl) txMgr).setRepository(this);
}
@Override
public TriggerManagement getTriggerManagement() {
return triggerMgr;
}
public void setTriggerManagement(TriggerManagement triggerMgr) {
this.triggerMgr = triggerMgr;
((TriggerManagementImpl) triggerMgr).setRepository(this);
}
@Override
public String getClientId() {
return thClient.get();
}
@Override
public String getUserName() {
String user = clientMgr.getCurrentUser();
//logger.info("getUserName; user: {}", user);
if (user == null) {
user = "unknown";
}
return user;
}
@Override
public Schema getSchema() {
return xdmSchema;
}
public void setSchema(Schema xdmSchema) {
// TODO: think about run-time updates..
this.xdmSchema = xdmSchema;
afterInit();
}
XQProcessor getXQProcessor() {
String clientId = thClient.get();
return getXQProcessor(clientId);
}
public XQProcessor getXQProcessor(String clientId) {
XQProcessor result;
if (clientId == null) {
result = newXQProcessor();
} else {
thClient.set(clientId);
result = processors.get(clientId);
if (result == null) {
result = newXQProcessor();
processors.put(clientId, result);
}
}
logger.trace("getXQProcessor; returning: {}, factory: {}, repo: {}",
result, result.getXQDataFactory(), result.getRepository());
return result;
}
private XQProcessor newXQProcessor() {
XQProcessor result = appContext.getBean(XQProcessor.class, this);
//XDMQueryManagement qMgr = getQueryManagement();
//result.setRepository(this);
return result;
}
public KeyFactory getFactory() {
return xdmFactory;
}
public void setFactory(KeyFactory factory) {
this.xdmFactory = factory;
}
private ContentHandler getHandler(String dataFormat) {
ContentHandler ch = handlers.get(dataFormat);
if (ch == null) {
DataFormat df = getDataFormat(dataFormat);
if (df != null) {
ch = instantiateClass(df.getHandlerClass());
}
if (ch == null) {
logger.info("getHandler; no handler found for dataFormat: {}", dataFormat);
String defaultFormat = this.xdmSchema.getProperty(pn_schema_format_default);
if ("json".equalsIgnoreCase(defaultFormat)) {
ch = new JsonpHandler(getModelManagement());
} else if ("xml".equalsIgnoreCase(defaultFormat)) {
ch = new XmlHandler(getModelManagement());
} else if ("map".equalsIgnoreCase(defaultFormat)) {
ch = new MapHandler(getModelManagement());
}
}
if (ch != null) {
ch.init(df.getProperties());
handlers.putIfAbsent(dataFormat, ch);
}
}
return ch;
}
@Override
public ContentParser getParser(String dataFormat) {
ContentHandler ch = getHandler(dataFormat);
if (ch != null) {
return ch.getParser();
}
return null;
}
@Override
public ContentBuilder getBuilder(String dataFormat) {
ContentHandler ch = getHandler(dataFormat);
if (ch != null) {
return ch.getBuilder();
}
return null;
}
@Override
public ContentModeler getModeler(String dataFormat) {
ContentHandler ch = getHandler(dataFormat);
if (ch != null) {
return ch.getModeler();
}
return null;
}
private <T> T instantiateClass(String className) {
try {
Class<?> clazz = Class.forName(className);
return (T) clazz.getConstructor(ModelManagement.class).newInstance(getModelManagement());
} catch (Exception ex) {
logger.error("instantiateClass; cannot instantiate: " + className, ex);
}
return null;
}
@Override
public void close() {
// TODO: disconnect all clients ?
}
public DataFormat getDataFormat(String dataFormat) {
// TODO: make it as fast as possible as it is called from many other places!
//logger.info("getDataFormat; format: {}", dataFormat);
Map<String, DataFormat> formats = xdmFormats;
if (formats == null) {
HazelcastInstance dataInstance = findSystemInstance();
if (dataInstance != null) {
formats = dataInstance.getMap(CN_SYS_FORMATS);
}
}
if (formats != null) {
// find by name
DataFormat df = formats.get(dataFormat);
if (df != null) {
return df;
} else {
// find by extension
for (DataFormat df2: formats.values()) {
if (df2.getExtensions().contains(dataFormat)) {
return df2;
}
}
}
// still not found -> get schema default format
dataFormat = this.xdmSchema.getProperty(pn_schema_format_default);
return formats.get(dataFormat);
}
return null;
}
public void setDataFormats(Collection<DataFormat> cFormats) {
if (cFormats != null) {
xdmFormats = new HashMap<>(cFormats.size());
for (DataFormat df: cFormats) {
xdmFormats.put(df.getName(), df);
}
}
}
public Collection<Library> getLibraries() {
if (xdmLibraries != null) {
return xdmLibraries;
}
HazelcastInstance dataInstance = findSystemInstance();
if (dataInstance != null && hasStorageMembers(dataInstance)) {
Map<String, Library> libraries = dataInstance.getMap(CN_SYS_LIBRARIES);
return libraries.values();
}
return Collections.emptyList();
}
public void setLibraries(Collection<Library> cLibraries) {
if (cLibraries != null) {
xdmLibraries = new ArrayList<>(cLibraries);
}
}
public Collection<Module> getModules() {
if (xdmModules != null) {
return xdmModules;
}
HazelcastInstance dataInstance = findSystemInstance();
if (dataInstance != null && hasStorageMembers(dataInstance)) {
Map<String, Module> modules = dataInstance.getMap(CN_SYS_MODULES);
return modules.values();
}
return Collections.emptyList();
}
public void setModules(Collection<Module> cModules) {
if (cModules != null) {
xdmModules = new ArrayList<>(cModules);
}
}
public void afterInit() {
Set<Index> indexes = xdmSchema.getIndexes();
if (indexes.size() > 0) {
for (Index idx: indexes) {
try {
indexMgr.createIndex(idx);
} catch (BagriException ex) {
logger.warn("afterInit.error; index: " + idx, ex);
}
}
}
// now init triggers..
Set<TriggerDefinition> triggers = xdmSchema.getTriggers();
if (triggers.size() > 0) {
for (TriggerDefinition trg: triggers) {
triggerMgr.createTrigger(trg);
}
}
}
public boolean isInitialized() {
if (xdmFormats != null && xdmModules != null && xdmLibraries != null) {
return true;
}
HazelcastInstance sysInstance = findSystemInstance();
if (sysInstance == null) {
return false;
}
return hasStorageMembers(sysInstance);
}
public boolean addSchemaIndex(Index index) {
if (xdmSchema.addIndex(index)) {
Path[] paths;
try {
paths = indexMgr.createIndex(index);
} catch (BagriException ex) {
logger.warn("addSchemaIndex.error; index: " + index, ex);
return false;
}
DocumentManagementImpl docMgr = (DocumentManagementImpl) getDocumentManagement();
for (Path xPath: paths) {
try {
docMgr.indexElements(xPath.getPathId());
} catch (BagriException ex) {
logger.warn("addSchemaIndex.error; index: " + index, ex);
}
}
return paths.length > 0;
}
logger.info("addSchemaIndex; index {} already exists! do we need to index values?", index);
return false;
}
public boolean dropSchemaIndex(String name) {
Index index = xdmSchema.removeIndex(name);
if (index != null) {
Path[] paths;
try {
paths = indexMgr.dropIndex(index);
} catch (BagriException ex) {
logger.warn("addSchemaIndex.error; index: " + index, ex);
return false;
}
DocumentManagementImpl docMgr = (DocumentManagementImpl) getDocumentManagement();
int cnt = 0;
List<Integer> pathIds = new ArrayList<>(paths.length);
for (Path xPath: paths) {
pathIds.add(xPath.getPathId());
cnt += docMgr.deindexElements(xPath.getPathId());
}
QueryManagementImpl queryMgr = (QueryManagementImpl) getQueryManagement();
Set<Integer> qKeys = queryMgr.getQueriesForPaths(pathIds, true);
if (!qKeys.isEmpty()) {
queryMgr.removeQueries(qKeys);
}
return cnt > 0;
}
logger.info("dropSchemaIndex; index {} does not exist?", index);
return false;
}
public boolean addSchemaTrigger(TriggerDefinition trigger) {
if (xdmSchema.addTrigger(trigger)) {
return triggerMgr.createTrigger(trigger);
}
return false;
}
public boolean dropSchemaTrigger(String className) {
TriggerDefinition trigger = xdmSchema.removeTrigger(className);
if (trigger != null) {
return triggerMgr.deleteTrigger(trigger);
}
return false;
}
}