package com.bagri.client.hazelcast.impl; import static com.bagri.core.Constants.*; import static com.bagri.core.server.api.CacheConstants.CN_XDM_QUERY; import static com.bagri.core.server.api.CacheConstants.CN_XDM_RESULT; import static com.bagri.core.server.api.CacheConstants.PN_XDM_SCHEMA_POOL; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.bagri.client.hazelcast.task.query.QueryExecutor; import com.bagri.client.hazelcast.task.query.QueryUrisProvider; import com.bagri.core.api.QueryManagement; import com.bagri.core.api.ResultCursor; import com.bagri.core.api.BagriException; import com.bagri.core.api.impl.QueryManagementBase; import com.bagri.core.model.Query; import com.bagri.core.model.QueryResult; import com.hazelcast.client.UndefinedErrorCodeException; import com.hazelcast.core.IExecutorService; import com.hazelcast.core.IMap; import com.hazelcast.core.Member; import com.hazelcast.core.ReplicatedMap; public class QueryManagementImpl extends QueryManagementBase implements QueryManagement { private final static Logger logger = LoggerFactory.getLogger(QueryManagementImpl.class); private boolean queryCache = true; private SchemaRepositoryImpl repo; private IExecutorService execService; private Future execution = null; private IMap<Long, QueryResult> resCache; private ReplicatedMap<Integer, Query> xqCache; public QueryManagementImpl() { // what should we do here? } void initialize(SchemaRepositoryImpl repo) { this.repo = repo; execService = repo.getHazelcastClient().getExecutorService(PN_XDM_SCHEMA_POOL); resCache = repo.getHazelcastClient().getMap(CN_XDM_RESULT); xqCache = repo.getHazelcastClient().getReplicatedMap(CN_XDM_QUERY); } public void setQueryCache(boolean queryCache) { this.queryCache = queryCache; } @Override public void cancelExecution() throws BagriException { if (execution != null) { // synchronize on it? if (!execution.isDone()) { execution.cancel(true); } } } @Override public Collection<String> getDocumentUris(String query, Map<String, Object> params, Properties props) throws BagriException { logger.trace("getDocumentUris.enter; query: {}", query); boolean useCache = this.queryCache; String qCache = props.getProperty(pn_client_queryCache); if (qCache != null) { useCache = Boolean.parseBoolean(qCache); } long qKey = getResultsKey(query, params); if (useCache) { QueryResult res = resCache.get(qKey); if (res != null) { logger.trace("getDocumentUris; got cached results: {}", res); return res.getDocUris(); } } QueryUrisProvider task = new QueryUrisProvider(repo.getClientId(), repo.getTransactionId(), query, params, props); Future<Collection<String>> future = execService.submit(task); execution = future; Collection<String> result = getResults(future, 0); logger.trace("getDocumentUris.exit; returning: {}", result); return result; } @Override public ResultCursor executeQuery(String query, Map<String, Object> params, Properties props) throws BagriException { logger.trace("executeQuery.enter; query: {}; bindings: {}; context: {}", query, params, props); boolean useCache = this.queryCache; if (props == null) { props = new Properties(); } String qCache = props.getProperty(pn_client_queryCache); if (qCache != null) { useCache = Boolean.parseBoolean(qCache); } long qKey = getResultsKey(query, params); if (useCache) { QueryResult res = resCache.get(qKey); if (res != null) { logger.trace("executeQuery; got cached results: {}", res); return new FixedCursorImpl(res.getResults()); } } props.setProperty(pn_client_id, repo.getClientId()); //props.setProperty(pn_client_txId, String.valueOf(repo.getTransactionId())); QueryExecutor task = new QueryExecutor(repo.getClientId(), repo.getTransactionId(), query, params, props); Future<ResultCursor> future; String runOn = props.getProperty(pn_client_submitTo, pv_client_submitTo_any); if (pv_client_submitTo_owner.equalsIgnoreCase(runOn)) { // not sure it'll use partition thread in this case! future = execService.submitToKeyOwner(task, qKey); } else if (pv_client_submitTo_member.equalsIgnoreCase(runOn)) { Member member = repo.getHazelcastClient().getPartitionService().getPartition(qKey).getOwner(); future = execService.submitToMember(task, member); } else { future = execService.submit(task); } execution = future; long timeout = Long.parseLong(props.getProperty(pn_xqj_queryTimeout, "0")); //if (cursor != null && cursor instanceof QueuedCursorImpl) { // purge queue, fetch/close current cursor..? //} ResultCursor cursor = getResults(future, timeout); logger.trace("execXQuery; got cursor: {}", cursor); if (cursor instanceof QueuedCursorImpl) { ((QueuedCursorImpl) cursor).deserialize(repo.getHazelcastClient()); } logger.trace("executeQuery.exit; returning: {}", cursor); return cursor; } private <T> T getResults(Future<T> future, long timeout) throws BagriException { T result; try { if (timeout > 0) { result = future.get(timeout, TimeUnit.MILLISECONDS); } else { result = future.get(); } return result; } catch (TimeoutException ex) { logger.warn("getResults.timeout; request timed out after {}; cancelled: {}", timeout, future.isCancelled()); future.cancel(true); throw new BagriException(ex, BagriException.ecQueryTimeout); } catch (InterruptedException | ExecutionException ex) { int errorCode = BagriException.ecQuery; if (ex.getCause() != null && ex.getCause() instanceof CancellationException) { errorCode = BagriException.ecQueryCancel; logger.warn("getResults.interrupted; request cancelled: {}", future.isCancelled()); } else { future.cancel(false); logger.error("getResults.error; error getting result", ex); Throwable err = ex; while (err.getCause() != null) { err = err.getCause(); if (err instanceof BagriException) { throw (BagriException) err; //} else if (err instanceof UndefinedErrorCodeException) { // if (XDMException.class.getName().equals(((UndefinedErrorCodeException) err).getOriginClassName())) { // throw new XDMException(ex.getMessage(), errorCode); // } } } } throw new BagriException(ex, errorCode); } } @Override public Collection<String> prepareQuery(String query) { //throws BagriException { logger.trace("prepareQuery.enter; query: {}", query); Collection<String> result = null; Query xq = xqCache.get(getQueryKey(query)); if (xq != null) { result = xq.getXdmQuery().getParamNames(); } logger.trace("prepareQuery.exit; returning: {}", result); return result; } }