/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.cache.query.internal.cq;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.logging.log4j.Logger;
import org.apache.geode.StatisticsFactory;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.query.CqClosedException;
import org.apache.geode.cache.query.CqEvent;
import org.apache.geode.cache.query.CqException;
import org.apache.geode.cache.query.CqExistsException;
import org.apache.geode.cache.query.CqState;
import org.apache.geode.cache.query.CqStatistics;
import org.apache.geode.cache.query.Query;
import org.apache.geode.cache.query.internal.CompiledIteratorDef;
import org.apache.geode.cache.query.internal.CompiledRegion;
import org.apache.geode.cache.query.internal.CompiledSelect;
import org.apache.geode.cache.query.internal.CompiledValue;
import org.apache.geode.cache.query.internal.CqQueryVsdStats;
import org.apache.geode.cache.query.internal.CqStateImpl;
import org.apache.geode.cache.query.internal.DefaultQuery;
import org.apache.geode.cache.query.internal.ExecutionContext;
import org.apache.geode.cache.query.internal.QueryExecutionContext;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.apache.geode.internal.cache.LocalRegion;
import org.apache.geode.internal.i18n.LocalizedStrings;
import org.apache.geode.internal.logging.InternalLogWriter;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.i18n.StringId;
/**
* Represents the CqQuery object. Implements CqQuery API and CqAttributeMutator.
*
* @since GemFire 5.5
*/
@SuppressWarnings("deprecation")
public abstract class CqQueryImpl implements InternalCqQuery {
private static final Logger logger = LogService.getLogger();
protected String cqName;
protected String queryString;
protected static final Object TOKEN = new Object();
protected LocalRegion cqBaseRegion;
protected Query query = null;
protected InternalLogWriter securityLogWriter;
protected CqServiceImpl cqService;
protected String regionName;
protected boolean isDurable = false;
// Stats counters
protected CqStatisticsImpl cqStats;
protected CqQueryVsdStats stats;
protected final CqStateImpl cqState = new CqStateImpl();
protected ExecutionContext queryExecutionContext = null;
public static TestHook testHook = null;
/**
* Constructor.
*/
public CqQueryImpl() {}
public CqQueryImpl(CqServiceImpl cqService, String cqName, String queryString,
boolean isDurable) {
this.cqName = cqName;
this.queryString = queryString;
this.securityLogWriter = (InternalLogWriter) cqService.getCache().getSecurityLoggerI18n();
this.cqService = cqService;
this.isDurable = isDurable;
}
/**
* returns CQ name
*/
public String getName() {
return this.cqName;
}
@Override
public void setName(String cqName) {
this.cqName = cqName;
}
public void setCqService(CqService cqService) {
this.cqService = (CqServiceImpl) cqService;
}
/**
* get the region name in CQ query
*/
@Override
public String getRegionName() {
return this.regionName;
}
public void updateCqCreateStats() {
// Initialize the VSD statistics
StatisticsFactory factory = cqService.getCache().getDistributedSystem();
this.stats = new CqQueryVsdStats(factory, getServerCqName());
this.cqStats = new CqStatisticsImpl(this);
// Update statistics with CQ creation.
this.cqService.stats.incCqsStopped();
this.cqService.stats.incCqsCreated();
this.cqService.stats.incCqsOnClient();
}
/**
* Validates the CQ. Checks for cq constraints. Also sets the base region name.
*/
public void validateCq() {
Cache cache = cqService.getCache();
DefaultQuery locQuery =
(DefaultQuery) ((GemFireCacheImpl) cache).getLocalQueryService().newQuery(this.queryString);
this.query = locQuery;
// assert locQuery != null;
// validate Query.
Object[] parameters = new Object[0]; // parameters are not permitted
// check that it is only a SELECT statement (possibly with IMPORTs)
CompiledSelect select = locQuery.getSimpleSelect();
if (select == null) {
throw new UnsupportedOperationException(
LocalizedStrings.CqQueryImpl_CQ_QUERIES_MUST_BE_A_SELECT_STATEMENT_ONLY
.toLocalizedString());
}
// must not be a DISTINCT select
if (select.isDistinct()) {
throw new UnsupportedOperationException(
LocalizedStrings.CqQueryImpl_SELECT_DISTINCT_QUERIES_NOT_SUPPORTED_IN_CQ
.toLocalizedString());
}
// get the regions referenced in this query
Set regionsInQuery = locQuery.getRegionsInQuery(parameters);
// check for more than one region is referenced in the query
// (though it could still be one region referenced multiple times)
if (regionsInQuery.size() > 1 || regionsInQuery.size() < 1) {
throw new UnsupportedOperationException(
LocalizedStrings.CqQueryImpl_CQ_QUERIES_MUST_REFERENCE_ONE_AND_ONLY_ONE_REGION
.toLocalizedString());
}
this.regionName = (String) regionsInQuery.iterator().next();
// make sure the where clause references no regions
Set regions = new HashSet();
CompiledValue whereClause = select.getWhereClause();
if (whereClause != null) {
whereClause.getRegionsInQuery(regions, parameters);
if (!regions.isEmpty()) {
throw new UnsupportedOperationException(
LocalizedStrings.CqQueryImpl_THE_WHERE_CLAUSE_IN_CQ_QUERIES_CANNOT_REFER_TO_A_REGION
.toLocalizedString());
}
}
List fromClause = select.getIterators();
// cannot have more than one iterator in FROM clause
if (fromClause.size() > 1) {
throw new UnsupportedOperationException(
LocalizedStrings.CqQueryImpl_CQ_QUERIES_CANNOT_HAVE_MORE_THAN_ONE_ITERATOR_IN_THE_FROM_CLAUSE
.toLocalizedString());
}
// the first iterator in the FROM clause must be just a CompiledRegion
CompiledIteratorDef itrDef = (CompiledIteratorDef) fromClause.get(0);
// By process of elimination, we know that the first iterator contains a reference
// to the region. Check to make sure it is only a CompiledRegion
if (!(itrDef.getCollectionExpr() instanceof CompiledRegion)) {
throw new UnsupportedOperationException(
LocalizedStrings.CqQueryImpl_CQ_QUERIES_MUST_HAVE_A_REGION_PATH_ONLY_AS_THE_FIRST_ITERATOR_IN_THE_FROM_CLAUSE
.toLocalizedString());
}
// must not have any projections
List projs = select.getProjectionAttributes();
if (projs != null) {
throw new UnsupportedOperationException(
LocalizedStrings.CqQueryImpl_CQ_QUERIES_DO_NOT_SUPPORT_PROJECTIONS.toLocalizedString());
}
// check the orderByAttrs, not supported
List orderBys = select.getOrderByAttrs();
if (orderBys != null) {
throw new UnsupportedOperationException(
LocalizedStrings.CqQueryImpl_CQ_QUERIES_DO_NOT_SUPPORT_ORDER_BY.toLocalizedString());
}
// Set Query ExecutionContext, that will be used in later execution.
this.setQueryExecutionContext(new QueryExecutionContext(null, this.cqService.getCache()));
}
/**
* Removes the CQ from CQ repository.
*
* @throws CqException
*/
protected void removeFromCqMap() throws CqException {
try {
cqService.removeCq(this.getServerCqName());
} catch (Exception ex) {
StringId errMsg =
LocalizedStrings.CqQueryImpl_FAILED_TO_REMOVE_CONTINUOUS_QUERY_FROM_THE_REPOSITORY_CQNAME_0_ERROR_1;
Object[] errMsgArgs = new Object[] {cqName, ex.getLocalizedMessage()};
String msg = errMsg.toLocalizedString(errMsgArgs);
logger.error(msg);
throw new CqException(msg, ex);
}
if (logger.isDebugEnabled()) {
logger.debug("Removed CQ from the CQ repository. CQ Name: {}", this.cqName);
}
}
/**
* Returns the QueryString of this CQ.
*/
public String getQueryString() {
return queryString;
}
/**
* Return the query after replacing region names with parameters
*
* @return the Query for the query string
*/
public Query getQuery() {
return query;
}
/**
* @see org.apache.geode.cache.query.CqQuery#getStatistics()
*/
public CqStatistics getStatistics() {
return cqStats;
}
/*
* (non-Javadoc)
*
* @see org.apache.geode.cache.query.internal.InternalCqQuery2#getCqBaseRegion()
*/
@Override
public LocalRegion getCqBaseRegion() {
return this.cqBaseRegion;
}
protected abstract void cleanup() throws CqException;
/**
* @return Returns the Region name on which this cq is created.
*/
public String getBaseRegionName() {
return this.regionName;
}
public abstract String getServerCqName();
/**
* Return the state of this query. Should not modify this state without first locking it.
*
* @return STOPPED RUNNING or CLOSED
*/
public CqState getState() {
return this.cqState;
}
/*
* (non-Javadoc)
*
* @see org.apache.geode.cache.query.internal.InternalCqQuery2#setCqState(int)
*/
@Override
public void setCqState(int state) {
if (this.isClosed()) {
throw new CqClosedException(
LocalizedStrings.CqQueryImpl_CQ_IS_CLOSED_CQNAME_0.toLocalizedString(this.cqName));
}
synchronized (cqState) {
if (state == CqStateImpl.RUNNING) {
if (this.isRunning()) {
// throw new
// IllegalStateException(LocalizedStrings.CqQueryImpl_CQ_IS_NOT_IN_RUNNING_STATE_STOP_CQ_DOES_NOT_APPLY_CQNAME_0
// .toLocalizedString(this.cqName));
}
this.cqState.setState(CqStateImpl.RUNNING);
this.cqService.stats.incCqsActive();
this.cqService.stats.decCqsStopped();
} else if (state == CqStateImpl.STOPPED) {
this.cqState.setState(CqStateImpl.STOPPED);
this.cqService.stats.incCqsStopped();
this.cqService.stats.decCqsActive();
} else if (state == CqStateImpl.CLOSING) {
this.cqState.setState(state);
}
}
}
/**
* Update CQ stats
*
* @param cqEvent object
*/
public void updateStats(CqEvent cqEvent) {
this.stats.updateStats(cqEvent); // Stats for VSD
}
/**
* Return true if the CQ is in running state
*
* @return true if running, false otherwise
*/
public boolean isRunning() {
return this.cqState.isRunning();
}
/**
* Return true if the CQ is in Sstopped state
*
* @return true if stopped, false otherwise
*/
public boolean isStopped() {
return this.cqState.isStopped();
}
/**
* Return true if the CQ is closed
*
* @return true if closed, false otherwise
*/
public boolean isClosed() {
return this.cqState.isClosed();
}
/**
* Return true if the CQ is in closing state.
*
* @return true if close in progress, false otherwise
*/
public boolean isClosing() {
return this.cqState.isClosing();
}
/**
* Return true if the CQ is durable
*
* @return true if durable, false otherwise
*/
public boolean isDurable() {
return this.isDurable;
}
/**
* Returns a reference to VSD stats of the CQ
*
* @return VSD stats of the CQ
*/
@Override
public CqQueryVsdStats getVsdStats() {
return stats;
}
public ExecutionContext getQueryExecutionContext() {
return queryExecutionContext;
}
public void setQueryExecutionContext(ExecutionContext queryExecutionContext) {
this.queryExecutionContext = queryExecutionContext;
}
/** Test Hook */
public interface TestHook {
public void pauseUntilReady();
public void ready();
public int numQueuedEvents();
public void setEventCount(int count);
}
}