/* * 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 org.apache.geode.InvalidDeltaException; import org.apache.geode.StatisticsFactory; import org.apache.geode.SystemFailure; import org.apache.geode.cache.*; import org.apache.geode.cache.client.Pool; import org.apache.geode.cache.client.internal.*; import org.apache.geode.cache.query.*; import org.apache.geode.cache.query.internal.*; import org.apache.geode.distributed.internal.DistributionAdvisor.Profile; import org.apache.geode.distributed.internal.DistributionConfig; import org.apache.geode.i18n.StringId; import org.apache.geode.internal.cache.CacheDistributionAdvisor.CacheProfile; import org.apache.geode.internal.cache.*; import org.apache.geode.internal.cache.tier.MessageType; import org.apache.geode.internal.cache.tier.sockets.CacheClientNotifier; import org.apache.geode.internal.cache.tier.sockets.CacheClientProxy; import org.apache.geode.internal.cache.tier.sockets.ClientProxyMembershipID; import org.apache.geode.internal.cache.tier.sockets.Part; import org.apache.geode.internal.i18n.LocalizedStrings; import org.apache.geode.internal.logging.LogService; import org.apache.geode.internal.logging.log4j.LocalizedMessage; import org.apache.logging.log4j.Logger; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; /** * @since GemFire 5.5 * * Implements the CqService functionality. * */ /** * */ public final class CqServiceImpl implements CqService { private static final Logger logger = LogService.getLogger(); private static final Integer MESSAGE_TYPE_LOCAL_CREATE = Integer.valueOf(MessageType.LOCAL_CREATE); private static final Integer MESSAGE_TYPE_LOCAL_UPDATE = Integer.valueOf(MessageType.LOCAL_UPDATE); private static final Integer MESSAGE_TYPE_LOCAL_DESTROY = Integer.valueOf(MessageType.LOCAL_DESTROY); private static final Integer MESSAGE_TYPE_EXCEPTION = Integer.valueOf(MessageType.EXCEPTION); /** * System property to evaluate the query even though the initial results are not required when cq * is executed using the execute() method. */ public static boolean EXECUTE_QUERY_DURING_INIT = Boolean .valueOf(System .getProperty(DistributionConfig.GEMFIRE_PREFIX + "cq.EXECUTE_QUERY_DURING_INIT", "true")) .booleanValue(); private static final String CQ_NAME_PREFIX = "GfCq"; private final Cache cache; /** * Manages cq pools to determine if a status of connect or disconnect needs to be sent out */ private final HashMap<String, Boolean> cqPoolsConnected = new HashMap<String, Boolean>(); /** * Manages CQ objects. uses serverCqName as key and CqQueryImpl as value * * @guarded.By cqQueryMapLock */ private volatile HashMap<String, CqQueryImpl> cqQueryMap = new HashMap<String, CqQueryImpl>(); private final Object cqQueryMapLock = new Object(); private volatile boolean isRunning = false; /** * Used by client when multiuser-authentication is true. */ private final HashMap<String, UserAttributes> cqNameToUserAttributesMap = new HashMap<String, UserAttributes>(); // private boolean isServer = true; /* * // Map to manage CQ to satisfied CQ events (keys) for optimizing updates. private final HashMap * cqToCqEventKeysMap = CqService.MAINTAIN_KEYS ? new HashMap() : null; */ // Map to manage the similar CQs (having same query - performance optimization). // With query as key and Set of CQs as values. private final ConcurrentHashMap matchingCqMap; // CQ Service statistics public final CqServiceStatisticsImpl cqServiceStats; public final CqServiceVsdStats stats; // CQ identifier, also used in auto generated CQ names private volatile long cqId = 1; /** * Used to synchronize access to CQs in the repository */ final Object cqSync = new Object(); /* This is to manage region to CQs map, client side book keeping. */ private HashMap<String, ArrayList<String>> baseRegionToCqNameMap = new HashMap<String, ArrayList<String>>(); /** * Access and modification to the contents of this map do not necessarily need to be lock * protected. This is just used to optimize construction of a server side cq name. Missing values * in this cache will mean a look up for a specific proxy id and cq name will miss and reconstruct * the string before adding it back to the cache */ private static final ConcurrentHashMap<String, ConcurrentHashMap<ClientProxyMembershipID, String>> serverCqNameCache = new ConcurrentHashMap<>(); /** * Constructor. * * @param c The cache used for the service */ public CqServiceImpl(final Cache c) { if (c == null) { throw new IllegalStateException(LocalizedStrings.CqService_CACHE_IS_NULL.toLocalizedString()); } GemFireCacheImpl gfc = (GemFireCacheImpl) c; gfc.getCancelCriterion().checkCancelInProgress(null); this.cache = gfc; // Initialize the Map which maintains the matching cqs. this.matchingCqMap = new ConcurrentHashMap<String, HashSet<String>>(); // Initialize the VSD statistics StatisticsFactory factory = cache.getDistributedSystem(); this.stats = new CqServiceVsdStats(factory); this.cqServiceStats = new CqServiceStatisticsImpl(this); // final LoggingThreadGroup group = // LoggingThreadGroup.createThreadGroup("CqExecutor Threads", logger); // if (this.cache.getCacheServers().isEmpty()) { // isServer = false; // } } /** * Returns the cache associated with the cqService. */ public Cache getCache() { return this.cache; } /* * (non-Javadoc) * * @see org.apache.geode.cache.query.internal.InternalCqService#newCq(java.lang.String, * java.lang.String, org.apache.geode.cache.query.CqAttributes, * org.apache.geode.cache.client.internal.ServerCQProxy, boolean) */ @Override public synchronized ClientCQ newCq(String cqName, String queryString, CqAttributes cqAttributes, InternalPool pool, boolean isDurable) throws QueryInvalidException, CqExistsException, CqException { if (queryString == null) { throw new IllegalArgumentException( LocalizedStrings.CqService_NULL_ARGUMENT_0.toLocalizedString("queryString")); } else if (cqAttributes == null) { throw new IllegalArgumentException( LocalizedStrings.CqService_NULL_ARGUMENT_0.toLocalizedString("cqAttribute")); } if (isServer()) { throw new IllegalStateException( LocalizedStrings.CqService_CLIENT_SIDE_NEWCQ_METHOD_INVOCATION_ON_SERVER .toLocalizedString()); } // Check if the given cq already exists. if (cqName != null && isCqExists(cqName)) { throw new CqExistsException( LocalizedStrings.CqService_CQ_WITH_THE_GIVEN_NAME_ALREADY_EXISTS_CQNAME_0 .toLocalizedString(cqName)); } ServerCQProxyImpl serverProxy = pool == null ? null : new ServerCQProxyImpl(pool); ClientCQImpl cQuery = new ClientCQImpl(this, cqName, queryString, cqAttributes, serverProxy, isDurable); cQuery.updateCqCreateStats(); // cQuery.initCq(); // Check if query is valid. cQuery.validateCq(); // Add cq into meta region. // Check if Name needs to be generated. if (cqName == null) { // in the case of cqname internally generated, the CqExistsException needs // to be taken care internally. while (true) { cQuery.setName(generateCqName()); try { addToCqMap(cQuery); } catch (CqExistsException ex) { if (logger.isDebugEnabled()) { logger.debug("Got CqExistsException while intializing cq : {} Error : {}", cQuery.getName(), ex.getMessage()); } continue; } break; } } else { addToCqMap(cQuery); } this.addToBaseRegionToCqNameMap(cQuery.getBaseRegionName(), cQuery.getServerCqName()); return cQuery; } /** * Executes the given CqQuery, if the CqQuery for that name is not there it registers the one and * executes. This is called on the Server. * * @param cqName * @param queryString * @param cqState * @param clientProxyId * @param ccn * @param manageEmptyRegions whether to update the 6.1 emptyRegions map held in the CCN * @param regionDataPolicy the data policy of the region associated with the query. This is only * needed if manageEmptyRegions is true. * @param emptyRegionsMap map of empty regions. * @throws IllegalStateException if this is called at client side. * @throws CqException */ @Override public synchronized ServerCQ executeCq(String cqName, String queryString, int cqState, ClientProxyMembershipID clientProxyId, CacheClientNotifier ccn, boolean isDurable, boolean manageEmptyRegions, int regionDataPolicy, Map emptyRegionsMap) throws CqException, RegionNotFoundException, CqClosedException { if (!isServer()) { throw new IllegalStateException( LocalizedStrings.CqService_SERVER_SIDE_EXECUTECQ_METHOD_IS_CALLED_ON_CLIENT_CQNAME_0 .toLocalizedString(cqName)); } String serverCqName = constructServerCqName(cqName, clientProxyId); ServerCQImpl cQuery = null; // If this CQ is not yet registered in Server, register CQ. if (!isCqExists(serverCqName)) { cQuery = new ServerCQImpl(this, cqName, queryString, isDurable, constructServerCqName(cqName, clientProxyId)); try { cQuery.registerCq(clientProxyId, ccn, cqState); if (manageEmptyRegions) { // new in 6.1 if (emptyRegionsMap != null && emptyRegionsMap.containsKey(cQuery.getBaseRegionName())) { regionDataPolicy = 0; } ccn.updateMapOfEmptyRegions( ccn.getClientProxy(clientProxyId, true).getRegionsWithEmptyDataPolicy(), cQuery.getBaseRegionName(), regionDataPolicy); } } catch (CqException cqe) { logger.info(LocalizedMessage.create( LocalizedStrings.CqService_EXCEPTION_WHILE_REGISTERING_CQ_ON_SERVER_CQNAME___0, cQuery.getName())); cQuery = null; throw cqe; } } else { cQuery = (ServerCQImpl) getCq(serverCqName); resumeCQ(cqState, cQuery); } if (logger.isDebugEnabled()) { logger.debug("Successfully created CQ on the server. CqName : {}", cQuery.getName()); } return cQuery; } public void resumeCQ(int cqState, ServerCQ cQuery) { // Initialize the state of CQ. if (((CqStateImpl) cQuery.getState()).getState() != cqState) { cQuery.setCqState(cqState); // addToCqEventKeysMap(cQuery); // Send state change info to peers. cQuery.getCqBaseRegion().getFilterProfile().setCqState(cQuery); } // If we are going to set the state to running, we need to check to see if it matches any other // cq if (cqState == CqStateImpl.RUNNING) { // Add to the matchedCqMap. addToMatchingCqMap((CqQueryImpl) cQuery); } } /* * public void addToCqEventKeysMap(CqQuery cq){ if (cqToCqEventKeysMap != null) { synchronized * (cqToCqEventKeysMap){ String serverCqName = ((CqQueryImpl)cq).getServerCqName(); if * (!cqToCqEventKeysMap.containsKey(serverCqName)){ cqToCqEventKeysMap.put(serverCqName, new * HashSet()); if (_logger.isDebugEnabled()) { * _logger.debug("CQ Event key maintenance for CQ, CqName: " + serverCqName + " is Enabled." + * " key maintenance map size is: " + cqToCqEventKeysMap.size()); } } } // synchronized } } */ public boolean hasCq() { HashMap<String, CqQueryImpl> cqMap = cqQueryMap; return (cqMap.size() > 0); } /** * Adds the given CQ and cqQuery object into the CQ map. */ public void addToCqMap(CqQueryImpl cq) throws CqExistsException, CqException { // On server side cqName will be server side cqName. String sCqName = cq.getServerCqName(); if (logger.isDebugEnabled()) { logger.debug("Adding to CQ Repository. CqName : {} ServerCqName : {}", cq.getName(), sCqName); } HashMap<String, CqQueryImpl> cqMap = cqQueryMap; if (cqMap.containsKey(sCqName)) { throw new CqExistsException( LocalizedStrings.CqService_A_CQ_WITH_THE_GIVEN_NAME_0_ALREADY_EXISTS .toLocalizedString(sCqName)); } synchronized (cqQueryMapLock) { HashMap<String, CqQueryImpl> tmpCqQueryMap = new HashMap<String, CqQueryImpl>(cqQueryMap); try { tmpCqQueryMap.put(sCqName, cq); } catch (Exception ex) { StringId errMsg = LocalizedStrings.CqQueryImpl_FAILED_TO_STORE_CONTINUOUS_QUERY_IN_THE_REPOSITORY_CQNAME_0_1; Object[] errMsgArgs = new Object[] {sCqName, ex.getLocalizedMessage()}; String s = errMsg.toLocalizedString(errMsgArgs); logger.error(s); throw new CqException(s, ex); } UserAttributes attributes = UserAttributes.userAttributes.get(); if (attributes != null) { this.cqNameToUserAttributesMap.put(cq.getName(), attributes); } cqQueryMap = tmpCqQueryMap; } } /** * Removes given CQ from the cqMap.. */ public void removeCq(String cqName) { // On server side cqName will be server side cqName. synchronized (cqQueryMapLock) { HashMap<String, CqQueryImpl> tmpCqQueryMap = new HashMap<String, CqQueryImpl>(cqQueryMap); tmpCqQueryMap.remove(cqName); this.cqNameToUserAttributesMap.remove(cqName); cqQueryMap = tmpCqQueryMap; } } /* * (non-Javadoc) * * @see * org.apache.geode.cache.query.internal.InternalCqService#getClientCqFromServer(org.apache.geode. * internal.cache.tier.sockets.ClientProxyMembershipID, java.lang.String) */ @Override public CqQuery getClientCqFromServer(ClientProxyMembershipID clientProxyId, String clientCqName) { // On server side cqName will be server side cqName. HashMap<String, CqQueryImpl> cqMap = cqQueryMap; return (CqQuery) cqMap.get(this.constructServerCqName(clientCqName, clientProxyId)); } /* * (non-Javadoc) * * @see org.apache.geode.cache.query.internal.InternalCqService#getCq(java.lang.String) */ @Override public InternalCqQuery getCq(String cqName) { // On server side cqName will be server side cqName. return (InternalCqQuery) cqQueryMap.get(cqName); } /** * Clears the CQ Query Map. */ public void clearCqQueryMap() { // On server side cqName will be server side cqName. synchronized (cqQueryMapLock) { cqQueryMap = new HashMap<String, CqQueryImpl>(); } } /* * (non-Javadoc) * * @see org.apache.geode.cache.query.internal.InternalCqService#getAllCqs() */ @Override public Collection<? extends InternalCqQuery> getAllCqs() { return cqQueryMap.values(); } /* * (non-Javadoc) * * @see org.apache.geode.cache.query.internal.InternalCqService#getAllCqs(java.lang.String) */ @Override public Collection<? extends InternalCqQuery> getAllCqs(final String regionName) throws CqException { if (regionName == null) { throw new IllegalArgumentException( LocalizedStrings.CqService_NULL_ARGUMENT_0.toLocalizedString("regionName")); } String[] cqNames = null; synchronized (this.baseRegionToCqNameMap) { ArrayList<String> cqs = this.baseRegionToCqNameMap.get(regionName); if (cqs == null) { return null; } cqNames = new String[cqs.size()]; cqs.toArray(cqNames); } ArrayList<InternalCqQuery> cQueryList = new ArrayList<InternalCqQuery>(); for (int cqCnt = 0; cqCnt < cqNames.length; cqCnt++) { InternalCqQuery cq = getCq(cqNames[cqCnt]); if (cq != null) { cQueryList.add(cq); } } return cQueryList; } /* * (non-Javadoc) * * @see org.apache.geode.cache.query.internal.InternalCqService#executeAllClientCqs() */ @Override public synchronized void executeAllClientCqs() throws CqException { executeCqs(this.getAllCqs()); } /* * (non-Javadoc) * * @see * org.apache.geode.cache.query.internal.InternalCqService#executeAllRegionCqs(java.lang.String) */ @Override public synchronized void executeAllRegionCqs(final String regionName) throws CqException { executeCqs(getAllCqs(regionName)); } /* * (non-Javadoc) * * @see * org.apache.geode.cache.query.internal.InternalCqService#executeCqs(org.apache.geode.cache.query * .CqQuery[]) */ @Override public synchronized void executeCqs(Collection<? extends InternalCqQuery> cqs) throws CqException { if (cqs == null) { return; } String cqName = null; for (InternalCqQuery internalCq : cqs) { CqQuery cq = (CqQuery) internalCq; if (!cq.isClosed() && cq.isStopped()) { try { cqName = cq.getName(); cq.execute(); } catch (QueryException qe) { if (logger.isDebugEnabled()) { logger.debug("Failed to execute the CQ, CqName : {} Error : {}", cqName, qe.getMessage()); } } catch (CqClosedException cce) { if (logger.isDebugEnabled()) { logger.debug("Failed to execute the CQ, CqName : {} Error : {}", cqName, cce.getMessage()); } } } } } /* * (non-Javadoc) * * @see org.apache.geode.cache.query.internal.InternalCqService#stopAllClientCqs() */ @Override public synchronized void stopAllClientCqs() throws CqException { stopCqs(this.getAllCqs()); } /* * (non-Javadoc) * * @see org.apache.geode.cache.query.internal.InternalCqService#stopAllRegionCqs(java.lang.String) */ @Override public synchronized void stopAllRegionCqs(final String regionName) throws CqException { stopCqs(this.getAllCqs(regionName)); } /* * (non-Javadoc) * * @see * org.apache.geode.cache.query.internal.InternalCqService#stopCqs(org.apache.geode.cache.query. * CqQuery[]) */ @Override public synchronized void stopCqs(Collection<? extends InternalCqQuery> cqs) throws CqException { final boolean isDebugEnabled = logger.isDebugEnabled(); if (isDebugEnabled) { if (cqs == null) { logger.debug("CqService.stopCqs cqs : null"); } else { logger.debug("CqService.stopCqs cqs : ({} queries)", cqs.size()); } } if (cqs == null) { return; } String cqName = null; for (InternalCqQuery internalCqQuery : cqs) { CqQuery cq = (CqQuery) internalCqQuery; if (!cq.isClosed() && cq.isRunning()) { try { cqName = cq.getName(); cq.stop(); } catch (QueryException qe) { if (isDebugEnabled) { logger.debug("Failed to stop the CQ, CqName : {} Error : {}", cqName, qe.getMessage()); } } catch (CqClosedException cce) { if (isDebugEnabled) { logger.debug("Failed to stop the CQ, CqName : {} Error : {}", cqName, cce.getMessage()); } } } } } /* * (non-Javadoc) * * @see org.apache.geode.cache.query.internal.InternalCqService#closeCqs(java.lang.String) */ @Override public void closeCqs(final String regionName) throws CqException { Collection<? extends InternalCqQuery> cqs = this.getAllCqs(regionName); if (cqs != null) { String cqName = null; for (InternalCqQuery cq : cqs) { try { cqName = cq.getName(); if (isServer()) { // invoked on the server cq.close(false); } else { // @todo grid: if regionName has a pool check its keepAlive boolean keepAlive = ((GemFireCacheImpl) this.cache).keepDurableSubscriptionsAlive(); if (cq.isDurable() && keepAlive) { logger.warn(LocalizedMessage.create( LocalizedStrings.CqService_NOT_SENDING_CQ_CLOSE_TO_THE_SERVER_AS_IT_IS_A_DURABLE_CQ)); cq.close(false); } else { cq.close(true); } } } catch (QueryException qe) { if (logger.isDebugEnabled()) { logger.debug("Failed to close the CQ, CqName : {} Error : {}", cqName, qe.getMessage()); } } catch (CqClosedException cce) { if (logger.isDebugEnabled()) { logger.debug("Failed to close the CQ, CqName : {} Error : {}", cqName, cce.getMessage()); } } } } } /** * Called directly on server side. * * @param cqName * @param clientId * @throws CqException */ @Override public void stopCq(String cqName, ClientProxyMembershipID clientId) throws CqException { String serverCqName = cqName; if (clientId != null) { serverCqName = this.constructServerCqName(cqName, clientId); removeFromCacheForServerToConstructedCQName(cqName, clientId); } ServerCQImpl cQuery = null; StringId errMsg = null; Exception ex = null; try { HashMap<String, CqQueryImpl> cqMap = cqQueryMap; if (!cqMap.containsKey(serverCqName)) { // throw new // CqException(LocalizedStrings.CqService_CQ_NOT_FOUND_FAILED_TO_STOP_THE_SPECIFIED_CQ_0.toLocalizedString(serverCqName)); /* * gregp 052808: We should silently fail here instead of throwing error. This is to deal * with races in recovery */ return; } cQuery = (ServerCQImpl) getCq(serverCqName); } catch (CacheLoaderException e1) { errMsg = LocalizedStrings.CqService_CQ_NOT_FOUND_IN_THE_CQ_META_REGION_CQNAME_0; ex = e1; } catch (TimeoutException e2) { errMsg = LocalizedStrings.CqService_TIMEOUT_WHILE_TRYING_TO_GET_CQ_FROM_META_REGION_CQNAME_0; ex = e2; } finally { if (ex != null) { String s = errMsg.toLocalizedString(cqName); if (logger.isDebugEnabled()) { logger.debug(s); } throw new CqException(s, ex); } } try { if (!cQuery.isStopped()) { cQuery.stop(); } } catch (CqClosedException cce) { throw new CqException(cce.getMessage()); } finally { // If this CQ is stopped, disable caching event keys for this CQ. // this.removeCQFromCaching(cQuery.getServerCqName()); this.removeFromMatchingCqMap(cQuery); } // Send stop message to peers. cQuery.getCqBaseRegion().getFilterProfile().stopCq(cQuery); } /* * (non-Javadoc) * * @see org.apache.geode.cache.query.internal.InternalCqService#closeCq(java.lang.String, * org.apache.geode.internal.cache.tier.sockets.ClientProxyMembershipID) */ @Override public void closeCq(String cqName, ClientProxyMembershipID clientProxyId) throws CqException { String serverCqName = cqName; if (clientProxyId != null) { serverCqName = this.constructServerCqName(cqName, clientProxyId); removeFromCacheForServerToConstructedCQName(cqName, clientProxyId); } ServerCQImpl cQuery = null; StringId errMsg = null; Exception ex = null; try { HashMap<String, CqQueryImpl> cqMap = cqQueryMap; if (!cqMap.containsKey(serverCqName)) { // throw new // CqException(LocalizedStrings.CqService_CQ_NOT_FOUND_FAILED_TO_CLOSE_THE_SPECIFIED_CQ_0 // .toLocalizedString(serverCqName)); /* * gregp 052808: We should silently fail here instead of throwing error. This is to deal * with races in recovery */ return; } cQuery = (ServerCQImpl) cqMap.get(serverCqName); } catch (CacheLoaderException e1) { errMsg = LocalizedStrings.CqService_CQ_NOT_FOUND_IN_THE_CQ_META_REGION_CQNAME_0; ex = e1; } catch (TimeoutException e2) { errMsg = LocalizedStrings.CqService_TIMEOUT_WHILE_TRYING_TO_GET_CQ_FROM_META_REGION_CQNAME_0; ex = e2; } finally { if (ex != null) { String s = errMsg.toLocalizedString(cqName); if (logger.isDebugEnabled()) { logger.debug(s); } throw new CqException(s, ex); } } try { cQuery.close(false); // Repository Region. // If CQ event caching is enabled, remove this CQs event cache reference. // removeCQFromCaching(serverCqName); // CqBaseRegion try { LocalRegion baseRegion = cQuery.getCqBaseRegion(); if (baseRegion != null && !baseRegion.isDestroyed()) { // Server specific clean up. if (isServer()) { FilterProfile fp = baseRegion.getFilterProfile(); if (fp != null) { fp.closeCq(cQuery); } CacheClientProxy clientProxy = cQuery.getCacheClientNotifier().getClientProxy(clientProxyId); clientProxy.decCqCount(); if (clientProxy.hasNoCq()) { this.stats.decClientsWithCqs(); } } } } catch (Exception e) { // May be cache is being shutdown if (logger.isDebugEnabled()) { logger.debug("Failed to remove CQ from the base region. CqName : {}", cqName); } } if (isServer()) { removeFromBaseRegionToCqNameMap(cQuery.getRegionName(), serverCqName); } LocalRegion baseRegion = cQuery.getCqBaseRegion(); if (baseRegion.getFilterProfile().getCqCount() <= 0) { if (logger.isDebugEnabled()) { logger.debug( "Should update the profile for this partitioned region {} for not requiring old value", baseRegion); } } } catch (CqClosedException cce) { throw new CqException(cce.getMessage()); } finally { this.removeFromMatchingCqMap(cQuery); } } /* * (non-Javadoc) * * @see org.apache.geode.cache.query.internal.InternalCqService#closeAllCqs(boolean) */ @Override public void closeAllCqs(boolean clientInitiated) { closeAllCqs(clientInitiated, getAllCqs()); } /** * Close all CQs executing in this VM, and release resources associated with executing CQs. * CqQuerys created by other VMs are unaffected. */ private void closeAllCqs(boolean clientInitiated, Collection<? extends InternalCqQuery> cqs) { closeAllCqs(clientInitiated, cqs, ((GemFireCacheImpl) this.cache).keepDurableSubscriptionsAlive()); } /* * (non-Javadoc) * * @see org.apache.geode.cache.query.internal.InternalCqService#closeAllCqs(boolean, * org.apache.geode.cache.query.CqQuery[], boolean) */ @Override public void closeAllCqs(boolean clientInitiated, Collection<? extends InternalCqQuery> cqs, boolean keepAlive) { // CqQuery[] cqs = getAllCqs(); if (cqs != null) { String cqName = null; if (logger.isDebugEnabled()) { logger.debug("Closing all CQs, number of CQ to be closed : {}", cqs.size()); } for (InternalCqQuery cQuery : cqs) { try { cqName = cQuery.getName(); // boolean keepAlive = ((GemFireCache)this.cache).keepDurableSubscriptionsAlive(); if (isServer()) { cQuery.close(false); } else { if (clientInitiated) { cQuery.close(true); } else { if (!isServer() && cQuery.isDurable() && keepAlive) { logger.warn(LocalizedMessage.create( LocalizedStrings.CqService_NOT_SENDING_CQ_CLOSE_TO_THE_SERVER_AS_IT_IS_A_DURABLE_CQ)); cQuery.close(false); } else { cQuery.close(true); } } } } catch (QueryException cqe) { if (!isRunning()) { // Not cache shutdown logger .warn(LocalizedMessage.create(LocalizedStrings.CqService_FAILED_TO_CLOSE_CQ__0___1, new Object[] {cqName, cqe.getMessage()})); } if (logger.isDebugEnabled()) { logger.debug(cqe.getMessage(), cqe); } } catch (CqClosedException cqe) { if (!isRunning()) { // Not cache shutdown logger .warn(LocalizedMessage.create(LocalizedStrings.CqService_FAILED_TO_CLOSE_CQ__0___1, new Object[] {cqName, cqe.getMessage()})); } if (logger.isDebugEnabled()) { logger.debug(cqe.getMessage(), cqe); } } } } } /* * (non-Javadoc) * * @see org.apache.geode.cache.query.internal.InternalCqService#getCqStatistics() */ @Override public CqServiceStatistics getCqStatistics() { return cqServiceStats; } /* * (non-Javadoc) * * @see org.apache.geode.cache.query.internal.InternalCqService#closeClientCqs(org.apache.geode. * internal.cache.tier.sockets.ClientProxyMembershipID) */ @Override public void closeClientCqs(ClientProxyMembershipID clientProxyId) throws CqException { final boolean isDebugEnabled = logger.isDebugEnabled(); if (isDebugEnabled) { logger.debug("Closing Client CQs for the client: {}", clientProxyId); } List<ServerCQ> cqs = getAllClientCqs(clientProxyId); for (ServerCQ cq : cqs) { CqQueryImpl cQuery = (CqQueryImpl) cq; try { cQuery.close(false); } catch (QueryException qe) { if (isDebugEnabled) { logger.debug("Failed to close the CQ, CqName : {} Error : {}", cQuery.getName(), qe.getMessage()); } } catch (CqClosedException cce) { if (isDebugEnabled) { logger.debug("Failed to close the CQ, CqName : {} Error : {}", cQuery.getName(), cce.getMessage()); } } } } /* * (non-Javadoc) * * @see org.apache.geode.cache.query.internal.InternalCqService#getAllClientCqs(org.apache.geode. * internal.cache.tier.sockets.ClientProxyMembershipID) */ @Override public List<ServerCQ> getAllClientCqs(ClientProxyMembershipID clientProxyId) { Collection<? extends InternalCqQuery> cqs = getAllCqs(); ArrayList<ServerCQ> clientCqs = new ArrayList<ServerCQ>(); for (InternalCqQuery cq : cqs) { ServerCQImpl cQuery = (ServerCQImpl) cq; ClientProxyMembershipID id = cQuery.getClientProxyId(); if (id != null && id.equals(clientProxyId)) { clientCqs.add(cQuery); } } return clientCqs; } /* * (non-Javadoc) * * @see * org.apache.geode.cache.query.internal.InternalCqService#getAllDurableClientCqs(org.apache.geode * .internal.cache.tier.sockets.ClientProxyMembershipID) */ @Override public List<String> getAllDurableClientCqs(ClientProxyMembershipID clientProxyId) throws CqException { if (clientProxyId == null) { throw new CqException( LocalizedStrings.CqService_UNABLE_TO_RETRIEVE_DURABLE_CQS_FOR_CLIENT_PROXY_ID .toLocalizedString(clientProxyId)); } List<ServerCQ> cqs = getAllClientCqs(clientProxyId); ArrayList<String> durableClientCqs = new ArrayList<String>(); for (ServerCQ cq : cqs) { ServerCQImpl cQuery = (ServerCQImpl) cq; if (cQuery != null && cQuery.isDurable()) { ClientProxyMembershipID id = cQuery.getClientProxyId(); if (id != null && id.equals(clientProxyId)) { durableClientCqs.add(cQuery.getName()); } } } return durableClientCqs; } /** * Server side method. Closes non-durable CQs for the given client proxy id. * * @param clientProxyId * @throws CqException */ @Override public void closeNonDurableClientCqs(ClientProxyMembershipID clientProxyId) throws CqException { final boolean isDebugEnabled = logger.isDebugEnabled(); if (isDebugEnabled) { logger.debug("Closing Client CQs for the client: {}", clientProxyId); } List<ServerCQ> cqs = getAllClientCqs(clientProxyId); for (ServerCQ cq : cqs) { ServerCQImpl cQuery = (ServerCQImpl) cq; try { if (!cQuery.isDurable()) { cQuery.close(false); } } catch (QueryException qe) { if (isDebugEnabled) { logger.debug("Failed to close the CQ, CqName : {} Error : {}", cQuery.getName(), qe.getMessage()); } } catch (CqClosedException cce) { if (isDebugEnabled) { logger.debug("Failed to close the CQ, CqName : {} Error : {}", cQuery.getName(), cce.getMessage()); } } } } /** * Is the CQ service in a cache server environment * * @return true if cache server, false otherwise */ public boolean isServer() { if (this.cache.getCacheServers().isEmpty()) { return false; } return true; } /** * Cleans up the CqService. */ @Override public void close() { if (logger.isDebugEnabled()) { logger.debug("Closing CqService. {}", this); } // Close All the CQs. // Need to take care when Clients are still connected... closeAllCqs(false); isRunning = false; } @Override public boolean isRunning() { return this.isRunning; } public void start() { this.isRunning = true; } /** * @return Returns the serverCqName. */ public String constructServerCqName(String cqName, ClientProxyMembershipID clientProxyId) { ConcurrentHashMap<ClientProxyMembershipID, String> cache = serverCqNameCache .computeIfAbsent(cqName, key -> new ConcurrentHashMap<ClientProxyMembershipID, String>()); String cName = cache.get(clientProxyId); if (null == cName) { final StringBuilder sb = new StringBuilder(cqName).append("__"); if (clientProxyId.isDurable()) { sb.append(clientProxyId.getDurableId()); } else { sb.append(clientProxyId.getDSMembership()); } cName = sb.toString(); cache.put(clientProxyId, cName); } return cName; } private void removeFromCacheForServerToConstructedCQName(final String cqName, ClientProxyMembershipID clientProxyMembershipID) { ConcurrentHashMap<ClientProxyMembershipID, String> cache = serverCqNameCache.get(cqName); if (cache != null) { cache.remove(clientProxyMembershipID); if (cache.size() == 0) { serverCqNameCache.remove(cqName); } } } /* * Checks if CQ with the given name already exists. * * @param cqName name of the CQ. * * @return true if exists else false. */ private synchronized boolean isCqExists(String cqName) { boolean status = false; HashMap<String, CqQueryImpl> cqMap = cqQueryMap; status = cqMap.containsKey(cqName); return status; } /* * Generates a name for CQ. Checks if CQ with that name already exists if so generates a new * cqName. */ public synchronized String generateCqName() { while (true) { String cqName = CQ_NAME_PREFIX + (cqId++); if (!isCqExists(cqName)) { return cqName; } } } /* * (non-Javadoc) * * @see * org.apache.geode.cache.query.internal.InternalCqService#dispatchCqListeners(java.util.HashMap, * int, java.lang.Object, java.lang.Object, byte[], * org.apache.geode.cache.client.internal.QueueManager, org.apache.geode.internal.cache.EventID) */ @Override public void dispatchCqListeners(HashMap<String, Integer> cqs, int messageType, Object key, Object value, byte[] delta, QueueManager qManager, EventID eventId) { ClientCQImpl cQuery = null; Object[] fullValue = new Object[1]; Iterator<Map.Entry<String, Integer>> iter = cqs.entrySet().iterator(); String cqName = null; final boolean isDebugEnabled = logger.isDebugEnabled(); while (iter.hasNext()) { try { Map.Entry<String, Integer> entry = iter.next(); cqName = entry.getKey(); cQuery = (ClientCQImpl) this.getCq(cqName); if (cQuery == null || (!cQuery.isRunning() && cQuery.getQueuedEvents() == null)) { if (isDebugEnabled) { logger.debug("Unable to invoke CqListener, {}, CqName : {}", ((cQuery == null) ? "CQ not found" : " CQ is Not running"), cqName); } continue; } Integer cqOp = (Integer) entry.getValue(); // If Region destroy event, close the cq. if (cqOp.intValue() == MessageType.DESTROY_REGION) { // The close will also invoke the listeners close(). try { cQuery.close(false); } catch (Exception ex) { // handle? } continue; } // Construct CqEvent. CqEventImpl cqEvent = null; cqEvent = new CqEventImpl(cQuery, getOperation(messageType), getOperation(cqOp.intValue()), key, value, delta, qManager, eventId); // Update statistics cQuery.updateStats(cqEvent); // Check if CQ Event needs to be queued. if (cQuery.getQueuedEvents() != null) { synchronized (cQuery.queuedEventsSynchObject) { // Get latest value. ConcurrentLinkedQueue<CqEventImpl> queuedEvents = cQuery.getQueuedEvents(); // Check to see, if its not set to null while waiting to get // Synchronization lock. if (queuedEvents != null) { if (isDebugEnabled) { logger.debug("Queueing event for key: {}", key); } cQuery.getVsdStats().incQueuedCqListenerEvents(); queuedEvents.add(cqEvent); continue; } } } this.invokeListeners(cqName, cQuery, cqEvent, fullValue); if (value == null) { value = fullValue[0]; } } // outer try catch (Throwable t) { logger.warn(LocalizedMessage .create(LocalizedStrings.CqService_ERROR_PROCESSING_CQLISTENER_FOR_CQ_0, cqName), t); if (t instanceof VirtualMachineError) { logger.warn(LocalizedMessage.create( LocalizedStrings.CqService_VIRTUALMACHINEERROR_PROCESSING_CQLISTENER_FOR_CQ_0, cqName), t); return; } } } // iteration. } public void invokeListeners(String cqName, ClientCQImpl cQuery, CqEventImpl cqEvent) { invokeListeners(cqName, cQuery, cqEvent, null); } public void invokeListeners(String cqName, ClientCQImpl cQuery, CqEventImpl cqEvent, Object[] fullValue) { if (!cQuery.isRunning() || cQuery.getCqAttributes() == null) { return; } // invoke CQ Listeners. CqListener[] cqListeners = cQuery.getCqAttributes().getCqListeners(); final boolean isDebugEnabled = logger.isDebugEnabled(); if (isDebugEnabled) { logger.debug("Invoking CQ listeners for {}, number of listeners : {} cqEvent : {}", cqName, cqListeners.length, cqEvent); } for (int lCnt = 0; lCnt < cqListeners.length; lCnt++) { try { // Check if the listener is not null, it could have been changed/reset // by the CqAttributeMutator. if (cqListeners[lCnt] != null) { cQuery.getVsdStats().incNumCqListenerInvocations(); try { if (cqEvent.getThrowable() != null) { cqListeners[lCnt].onError(cqEvent); } else { cqListeners[lCnt].onEvent(cqEvent); } } catch (InvalidDeltaException ide) { if (isDebugEnabled) { logger.debug("CqService.dispatchCqListeners(): Requesting full value..."); } Part result = (Part) GetEventValueOp .executeOnPrimary(cqEvent.getQueueManager().getPool(), cqEvent.getEventID(), null); Object newVal = null; if (result == null || (newVal = result.getObject()) == null) { if (!cache.getCancelCriterion().isCancelInProgress()) { Exception ex = new Exception("Failed to retrieve full value from server for eventID " + cqEvent.getEventID()); logger.warn(LocalizedMessage.create( LocalizedStrings.CqService_EXCEPTION_IN_THE_CQLISTENER_OF_THE_CQ_CQNAME_0_ERROR__1, new Object[] {cqName, ex.getMessage()})); if (isDebugEnabled) { logger.debug(ex.getMessage(), ex); } } } else { ((GemFireCacheImpl) this.cache).getCachePerfStats().incDeltaFullValuesRequested(); cqEvent = new CqEventImpl(cQuery, cqEvent.getBaseOperation(), cqEvent.getQueryOperation(), cqEvent.getKey(), newVal, cqEvent.getDeltaValue(), cqEvent.getQueueManager(), cqEvent.getEventID()); if (cqEvent.getThrowable() != null) { cqListeners[lCnt].onError(cqEvent); } else { cqListeners[lCnt].onEvent(cqEvent); } if (fullValue != null) { fullValue[0] = newVal; } } } } // Handle client side exceptions. } catch (Exception ex) { if (!cache.getCancelCriterion().isCancelInProgress()) { logger.warn(LocalizedMessage.create( LocalizedStrings.CqService_EXCEPTION_IN_THE_CQLISTENER_OF_THE_CQ_CQNAME_0_ERROR__1, new Object[] {cqName, ex.getMessage()})); if (isDebugEnabled) { logger.debug(ex.getMessage(), ex); } } } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (Throwable t) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.warn(LocalizedMessage.create( LocalizedStrings.CqService_RUNTIME_EXCEPTION_IN_THE_CQLISTENER_OF_THE_CQ_CQNAME_0_ERROR__1, new Object[] {cqName, t.getLocalizedMessage()})); if (isDebugEnabled) { logger.debug(t.getMessage(), t); } } } } public void invokeCqConnectedListeners(String cqName, ClientCQImpl cQuery, boolean connected) { if (!cQuery.isRunning() || cQuery.getCqAttributes() == null) { return; } cQuery.setConnected(connected); // invoke CQ Listeners. CqListener[] cqListeners = cQuery.getCqAttributes().getCqListeners(); if (logger.isDebugEnabled()) { logger.debug("Invoking CQ status listeners for {}, number of listeners : {}", cqName, cqListeners.length); } for (int lCnt = 0; lCnt < cqListeners.length; lCnt++) { try { if (cqListeners[lCnt] != null) { if (cqListeners[lCnt] instanceof CqStatusListener) { CqStatusListener listener = (CqStatusListener) cqListeners[lCnt]; if (connected) { listener.onCqConnected(); } else { listener.onCqDisconnected(); } } } // Handle client side exceptions. } catch (Exception ex) { if (!cache.getCancelCriterion().isCancelInProgress()) { logger.warn(LocalizedMessage.create( LocalizedStrings.CqService_EXCEPTION_IN_THE_CQLISTENER_OF_THE_CQ_CQNAME_0_ERROR__1, new Object[] {cqName, ex.getMessage()})); if (logger.isDebugEnabled()) { logger.debug(ex.getMessage(), ex); } } } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (Throwable t) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.warn(LocalizedMessage.create( LocalizedStrings.CqService_RUNTIME_EXCEPTION_IN_THE_CQLISTENER_OF_THE_CQ_CQNAME_0_ERROR__1, new Object[] {cqName, t.getLocalizedMessage()})); if (logger.isDebugEnabled()) { logger.debug(t.getMessage(), t); } } } } /** * Returns the Operation for the given EnumListenerEvent type. * * @param eventType * @return Operation */ private Operation getOperation(int eventType) { Operation op = null; switch (eventType) { case MessageType.LOCAL_CREATE: op = Operation.CREATE; break; case MessageType.LOCAL_UPDATE: op = Operation.UPDATE; break; case MessageType.LOCAL_DESTROY: op = Operation.DESTROY; break; case MessageType.LOCAL_INVALIDATE: op = Operation.INVALIDATE; break; case MessageType.CLEAR_REGION: op = Operation.REGION_CLEAR; break; case MessageType.INVALIDATE_REGION: op = Operation.REGION_INVALIDATE; break; } return op; } /* * (non-Javadoc) * * @see * org.apache.geode.cache.query.internal.InternalCqService#processEvents(org.apache.geode.cache. * CacheEvent, org.apache.geode.distributed.internal.DistributionAdvisor.Profile, * org.apache.geode.distributed.internal.DistributionAdvisor.Profile[], * org.apache.geode.internal.cache.FilterRoutingInfo) */ @Override public void processEvents(CacheEvent event, Profile localProfile, Profile[] profiles, FilterRoutingInfo frInfo) throws CqException { // Is this a region event or an entry event if (event instanceof RegionEvent) { processRegionEvent(event, localProfile, profiles, frInfo); } else { // Use the PDX types in serialized form. DefaultQuery.setPdxReadSerialized(this.cache, true); try { processEntryEvent(event, localProfile, profiles, frInfo); } finally { DefaultQuery.setPdxReadSerialized(this.cache, false); } } } private void processRegionEvent(CacheEvent event, Profile localProfile, Profile[] profiles, FilterRoutingInfo frInfo) throws CqException { final boolean isDebugEnabled = logger.isDebugEnabled(); if (isDebugEnabled) { logger.debug("CQ service processing region event {}", event); } Integer cqRegionEvent = generateCqRegionEvent(event); for (int i = -1; i < profiles.length; i++) { CacheProfile cf; if (i < 0) { cf = (CacheProfile) localProfile; if (cf == null) continue; } else { cf = (CacheProfile) profiles[i]; } FilterProfile pf = cf.filterProfile; if (pf == null || pf.getCqMap().isEmpty()) { continue; } Map cqs = pf.getCqMap(); HashMap<Long, Integer> cqInfo = new HashMap<Long, Integer>(); Iterator cqIter = cqs.entrySet().iterator(); while (cqIter.hasNext()) { Map.Entry cqEntry = (Map.Entry) cqIter.next(); ServerCQImpl cQuery = (ServerCQImpl) cqEntry.getValue(); if (!event.isOriginRemote() && event.getOperation().isRegionDestroy() && !((LocalRegion) event.getRegion()).isUsedForPartitionedRegionBucket()) { try { if (isDebugEnabled) { logger.debug("Closing CQ on region destroy event. CqName : {}", cQuery.getName()); } cQuery.close(false); } catch (Exception ex) { if (isDebugEnabled) { logger.debug("Failed to Close CQ on region destroy. CqName : {}", cQuery.getName(), ex); } } } cqInfo.put(cQuery.getFilterID(), cqRegionEvent); cQuery.getVsdStats().updateStats(cqRegionEvent); } if (pf.isLocalProfile()) { frInfo.setLocalCqInfo(cqInfo); } else { frInfo.setCqRoutingInfo(cf.getDistributedMember(), cqInfo); } } } private void processEntryEvent(CacheEvent event, Profile localProfile, Profile[] profiles, FilterRoutingInfo frInfo) throws CqException { final boolean isDebugEnabled = logger.isDebugEnabled(); HashSet<Object> cqUnfilteredEventsSet_newValue = new HashSet<Object>(); HashSet<Object> cqUnfilteredEventsSet_oldValue = new HashSet<Object>(); boolean b_cqResults_newValue = false; boolean b_cqResults_oldValue = false; boolean queryOldValue; EntryEvent entryEvent = (EntryEvent) event; Object eventKey = entryEvent.getKey(); boolean isDupEvent = ((EntryEventImpl) event).isPossibleDuplicate(); // The CQ query needs to be applied when the op is update, destroy // invalidate and in case when op is create and its an duplicate // event, the reason for this is when peer sends a duplicate event // it marks it as create and sends it, so that the receiving node // applies it (see DR.virtualPut()). boolean opRequiringQueryOnOldValue = (event.getOperation().isUpdate() || event.getOperation().isDestroy() || event.getOperation().isInvalidate() || (event.getOperation().isCreate() && isDupEvent)); HashMap<String, Integer> matchedCqs = new HashMap<String, Integer>(); long executionStartTime = 0; for (int i = -1; i < profiles.length; i++) { CacheProfile cf; if (i < 0) { cf = (CacheProfile) localProfile; if (cf == null) continue; } else { cf = (CacheProfile) profiles[i]; } FilterProfile pf = cf.filterProfile; if (pf == null || pf.getCqMap().isEmpty()) { continue; } Map cqs = pf.getCqMap(); if (isDebugEnabled) { logger.debug("Profile for {} processing {} CQs", cf.peerMemberId, cqs.size()); } if (cqs.isEmpty()) { continue; } // Get new value. If its not retrieved. if (cqUnfilteredEventsSet_newValue.isEmpty() && (event.getOperation().isCreate() || event.getOperation().isUpdate())) { Object newValue = entryEvent.getNewValue(); if (newValue != null) { // We have a new value to run the query on cqUnfilteredEventsSet_newValue.add(newValue); } } HashMap<Long, Integer> cqInfo = new HashMap<Long, Integer>(); Iterator cqIter = cqs.entrySet().iterator(); while (cqIter.hasNext()) { Map.Entry cqEntry = (Map.Entry) cqIter.next(); ServerCQImpl cQuery = (ServerCQImpl) cqEntry.getValue(); b_cqResults_newValue = false; b_cqResults_oldValue = false; queryOldValue = false; if (cQuery == null) { continue; } String cqName = cQuery.getServerCqName(); Long filterID = cQuery.getFilterID(); if (isDebugEnabled) { logger.debug("Processing CQ : {} Key: {}", cqName, eventKey); } Integer cqEvent = null; if (matchedCqs.containsKey(cqName)) { cqEvent = matchedCqs.get(cqName); if (isDebugEnabled) { logger.debug("query {} has already been processed and returned {}", cqName, cqEvent); } if (cqEvent == null) { continue; } // Update the Cache Results for this CQ. if (cqEvent.intValue() == MessageType.LOCAL_CREATE || cqEvent.intValue() == MessageType.LOCAL_UPDATE) { cQuery.addToCqResultKeys(eventKey); } else if (cqEvent.intValue() == MessageType.LOCAL_DESTROY) { cQuery.markAsDestroyedInCqResultKeys(eventKey); } } else { boolean error = false; // synchronized (cQuery) { try { synchronized (cQuery) { // Apply query on new value. if (!cqUnfilteredEventsSet_newValue.isEmpty()) { executionStartTime = this.stats.startCqQueryExecution(); b_cqResults_newValue = evaluateQuery(cQuery, new Object[] {cqUnfilteredEventsSet_newValue}); this.stats.endCqQueryExecution(executionStartTime); } } // In case of Update, destroy and invalidate. // Apply query on oldValue. if (opRequiringQueryOnOldValue) { // Check if CQ Result is cached, if not apply query on old // value. Currently the CQ Results are not cached for the // Partitioned Regions. Once this is added remove the check // with PR region. if (cQuery.cqResultKeysInitialized) { b_cqResults_oldValue = cQuery.isPartOfCqResult(eventKey); // For PR if not found in cache, apply the query on old value. // Also apply if the query was not executed during cq execute if ((cQuery.isPR || !CqServiceImpl.EXECUTE_QUERY_DURING_INIT) && b_cqResults_oldValue == false) { queryOldValue = true; } if (isDebugEnabled && !cQuery.isPR && !b_cqResults_oldValue) { logger.debug( "Event Key not found in the CQ Result Queue. EventKey : {} CQ Name : {}", eventKey, cqName); } } else { queryOldValue = true; } if (queryOldValue) { if (cqUnfilteredEventsSet_oldValue.isEmpty()) { Object oldValue = entryEvent.getOldValue(); if (oldValue != null) { cqUnfilteredEventsSet_oldValue.add(oldValue); } } synchronized (cQuery) { // Apply query on old value. if (!cqUnfilteredEventsSet_oldValue.isEmpty()) { executionStartTime = this.stats.startCqQueryExecution(); b_cqResults_oldValue = evaluateQuery(cQuery, new Object[] {cqUnfilteredEventsSet_oldValue}); this.stats.endCqQueryExecution(executionStartTime); } else { if (isDebugEnabled) { logger.debug( "old value for event with key {} is null - query execution not performed", eventKey); } } } } // Query oldValue } } catch (Exception ex) { // Any exception in running the query should be caught here and // buried because this code is running in-line with the message // processing code and we don't want to kill that thread error = true; // CHANGE LOG MESSAGE: logger.info(LocalizedMessage.create( LocalizedStrings.CqService_ERROR_WHILE_PROCESSING_CQ_ON_THE_EVENT_KEY_0_CQNAME_1_ERROR_2, new Object[] {((EntryEvent) event).getKey(), cQuery.getName(), ex.getLocalizedMessage()})); } if (error) { cqEvent = MESSAGE_TYPE_EXCEPTION; } else { if (b_cqResults_newValue) { if (b_cqResults_oldValue) { cqEvent = MESSAGE_TYPE_LOCAL_UPDATE; } else { cqEvent = MESSAGE_TYPE_LOCAL_CREATE; } // If its create and caching is enabled, cache the key // for this CQ. cQuery.addToCqResultKeys(eventKey); } else if (b_cqResults_oldValue) { // Base invalidate operation is treated as destroy. // When the invalidate comes through, the entry will no longer // satisfy the query and will need to be deleted. cqEvent = MESSAGE_TYPE_LOCAL_DESTROY; // If caching is enabled, mark this event's key as removed // from the CQ cache. cQuery.markAsDestroyedInCqResultKeys(eventKey); } } } // end synchronized(cQuery) // Get the matching CQs if any. // synchronized (this.matchingCqMap){ String query = cQuery.getQueryString(); Set matchingCqs = (Set) matchingCqMap.get(query); if (matchingCqs != null) { Iterator iter = matchingCqs.iterator(); while (iter.hasNext()) { String matchingCqName = (String) iter.next(); if (!matchingCqName.equals(cqName)) { matchedCqs.put(matchingCqName, cqEvent); if (isDebugEnabled) { logger.debug("Adding CQ into Matching CQ Map: {} Event is: {}", matchingCqName, cqEvent); } } } } // } } if (cqEvent != null && cQuery.isRunning()) { if (isDebugEnabled) { logger.debug("Added event to CQ with client-side name: {} key: {} operation : {}", cQuery.cqName, eventKey, cqEvent); } cqInfo.put(filterID, cqEvent); CqQueryVsdStats stats = cQuery.getVsdStats(); if (stats != null) { stats.updateStats(cqEvent); } } } if (cqInfo.size() > 0) { if (pf.isLocalProfile()) { if (isDebugEnabled) { logger.debug("Setting local CQ matches to {}", cqInfo); } frInfo.setLocalCqInfo(cqInfo); } else { if (isDebugEnabled) { logger.debug("Setting CQ matches for {} to {}", cf.getDistributedMember(), cqInfo); } frInfo.setCqRoutingInfo(cf.getDistributedMember(), cqInfo); } } } // iteration over Profiles. } /* * public void processEvents (EnumListenerEvent operation, CacheEvent event, ClientUpdateMessage * clientMessage, CM<ClientProxyMembershipID, CM<CqQuery, Boolean>> clientIds) throws CqException * { * * //Is this a region event or an entry event if (event instanceof RegionEvent){ * processRegionEvent(operation, event, clientMessage, clientIds); } else { processEntryEvent * (operation, event, clientMessage, clientIds); } * * } * * private void processRegionEvent(EnumListenerEvent operation, CacheEvent event, * ClientUpdateMessage clientMessage, CM<ClientProxyMembershipID, CM<CqQuery, Boolean>> clientIds) * throws CqException { * * if (logger.isDebugEnabled()) { logger.debug("Processing region event for region " + * ((LocalRegion)(event.getRegion())).getName()); } HashMap filteredCqs = new HashMap(); Integer * cqRegionEvent = generateCqRegionEvent(operation); Iterator it = * clientIds.entrySet().iterator(); while (it.hasNext()) { Map.Entry me = (Map.Entry)it.next(); * ClientProxyMembershipID clientId = (ClientProxyMembershipID)me.getKey(); CM cqsToBooleans = * (CM)me.getValue(); if (cqsToBooleans == null) { continue; } Set<CqQuery> cqs = * cqsToBooleans.keySet(); if (cqs.isEmpty()) { continue; } filteredCqs.clear(); Iterator cqIt = * cqs.iterator(); while (cqIt.hasNext()) { CqQueryImpl cQuery = (CqQueryImpl)cqIt.next(); if * (operation == EnumListenerEvent.AFTER_REGION_DESTROY) { try { if (logger.isDebugEnabled()){ * logger.debug("Closing CQ on region destroy event. CqName :" + cQuery.getName()); } * cQuery.close(false); } catch (Exception ex) { * logger.debug("Failed to Close CQ on region destroy. CqName :" + cQuery.getName(), ex); } * * } filteredCqs.put(cQuery.cqName, cqRegionEvent); * cQuery.getVsdStats().updateStats(cqRegionEvent); * * } if (!filteredCqs.isEmpty()){ ((ClientUpdateMessageImpl)clientMessage).addClientCqs( clientId, * filteredCqs); } * * } * * } * * private void processEntryEvent(EnumListenerEvent operation, CacheEvent event, * ClientUpdateMessage clientMessage, CM<ClientProxyMembershipID, CM<CqQuery, Boolean>> clientIds) * throws CqException { HashSet cqUnfilteredEventsSet_newValue = new HashSet(); HashSet * cqUnfilteredEventsSet_oldValue = new HashSet(); boolean b_cqResults_newValue = false; boolean * b_cqResults_oldValue = false; EntryEvent entryEvent = (EntryEvent)event; Object eventKey = * entryEvent.getKey(); if (operation == EnumListenerEvent.AFTER_CREATE || operation == * EnumListenerEvent.AFTER_UPDATE) { if (entryEvent.getNewValue() != null) { //We have a new value * to run the query on cqUnfilteredEventsSet_newValue.clear(); * cqUnfilteredEventsSet_newValue.add(entryEvent.getNewValue()); } } * * HashMap matchedCqs = new HashMap(); long executionStartTime = 0; Iterator it = * clientIds.entrySet().iterator(); while (it.hasNext()) { Map.Entry me = (Map.Entry)it.next(); * ClientProxyMembershipID clientId = (ClientProxyMembershipID)me.getKey(); if * (logger.isDebugEnabled()) { logger.debug("Processing event for CQ filter, ClientId : " + * clientId); } CM cqsToBooleans = (CM)me.getValue(); if (cqsToBooleans == null) { continue; } * Set<CqQuery> cqs = cqsToBooleans.keySet(); if (cqs.isEmpty()) { continue; } HashMap filteredCqs * = new HashMap(); Iterator cqIt = cqs.iterator(); while (cqIt.hasNext()) { CqQueryImpl cQuery = * (CqQueryImpl)cqIt.next(); b_cqResults_newValue = false; b_cqResults_oldValue = false; if * (cQuery == null || !(cQuery.isRunning())){ continue; } String cqName = * cQuery.getServerCqName(); Integer cqEvent = null; if (matchedCqs.containsKey(cqName)) { if * (logger.isDebugEnabled()){ logger. * debug("Similar cq/query is already processed, getting the cq event-type from the matched cq."); * } cqEvent = (Integer)matchedCqs.get(cqName); } else { boolean error = false; boolean * hasSeenEvent = false; HashSet cqEventKeys = null; synchronized (cQuery) { try { // Apply query * on new value. if (!cqUnfilteredEventsSet_newValue.isEmpty()) { executionStartTime = * this.stats.startCqQueryExecution(); b_cqResults_newValue = evaluateQuery(cQuery, new Object[] * {cqUnfilteredEventsSet_newValue}); this.stats.endCqQueryExecution(executionStartTime); } // * Check if old value is cached, if not apply query on old value. if (cqToCqEventKeysMap != null) * { synchronized (cqToCqEventKeysMap) { if ((cqEventKeys = * (HashSet)cqToCqEventKeysMap.get(cqName)) != null) { hasSeenEvent = * cqEventKeys.contains(eventKey); } } } if (!hasSeenEvent) { // get the oldValue. // In case of * Update, destroy and invalidate. if (operation == EnumListenerEvent.AFTER_UPDATE || operation == * EnumListenerEvent.AFTER_DESTROY || operation == EnumListenerEvent.AFTER_INVALIDATE) { if * (entryEvent.getOldValue() != null) { cqUnfilteredEventsSet_oldValue.clear(); * cqUnfilteredEventsSet_oldValue.add(entryEvent.getOldValue()); // Apply query on old value. * executionStartTime = this.stats.startCqQueryExecution(); b_cqResults_oldValue = * evaluateQuery(cQuery, new Object[] {cqUnfilteredEventsSet_oldValue}); * this.stats.endCqQueryExecution(executionStartTime); } } } } catch (Exception ex) { //Any * exception in running the query // should be caught here and buried //because this code is * running inline with the //message processing code and we don't want to //kill that thread error * = true; logger.info( LocalizedStrings. * CqService_ERROR_WHILE_PROCESSING_CQ_ON_THE_EVENT_KEY_0_CQNAME_1_CLIENTID_2_ERROR_3, new * Object[] { ((EntryEvent)event).getKey(), cQuery.getName(), clientId, * ex.getLocalizedMessage()}); } * * if (error) { cqEvent = Integer.valueOf(MessageType.EXCEPTION); } else { if * (b_cqResults_newValue) { if (hasSeenEvent || b_cqResults_oldValue) { cqEvent = * Integer.valueOf(MessageType.LOCAL_UPDATE); } else { cqEvent = * Integer.valueOf(MessageType.LOCAL_CREATE); } // If its create and caching is enabled, cache the * key for this CQ. if (!hasSeenEvent && cqEventKeys != null) { cqEventKeys.add(eventKey); } } * else if (hasSeenEvent || (b_cqResults_oldValue)) { // Base invalidate operation is treated as * destroy. // When the invalidate comes through, the entry will no longer satisfy // the query * and will need to be deleted. cqEvent = Integer.valueOf(MessageType.LOCAL_DESTROY); // If * caching is enabled, remove this event's key from the cache. if (hasSeenEvent && cqEventKeys != * null) { cqEventKeys.remove(eventKey); } } } * * } //end synchronized(cQuery) * * // Get the matching CQs if any. synchronized (this.matchingCqMap){ String query = * cQuery.getQueryString(); ArrayList matchingCqs = (ArrayList)matchingCqMap.get(query); if * (matchingCqs != null) { Iterator iter = matchingCqs.iterator(); while (iter.hasNext()) { String * matchingCqName = (String)iter.next(); if (!matchingCqName.equals(cqName)){ * matchedCqs.put(matchingCqName, cqEvent); } } } } * * } * * if (cqEvent != null){ if (logger.isDebugEnabled()) { * logger.debug("Event is added for the CQ, CqName (clientside): " + cQuery.cqName + * " With CQ Op : " + cqEvent + " for Client : " + clientId); } filteredCqs.put(cQuery.cqName, * cqEvent); cQuery.getVsdStats().updateStats(cqEvent); } * * } // iteration over cqsToBooleans.keySet() if (!filteredCqs.isEmpty()){ * logger.debug("Adding event map for client : "+clientId + * " with event map size : "+filteredCqs.size()); * ((ClientUpdateMessageImpl)clientMessage).addClientCqs(clientId, filteredCqs); } } // iteration * over clientIds.entrySet() } */ private Integer generateCqRegionEvent(CacheEvent event) { Integer cqEvent = null; if (event.getOperation().isRegionDestroy()) { cqEvent = Integer.valueOf(MessageType.DESTROY_REGION); } else if (event.getOperation().isRegionInvalidate()) { cqEvent = Integer.valueOf(MessageType.INVALIDATE_REGION); } else if (event.getOperation().isClear()) { cqEvent = Integer.valueOf(MessageType.CLEAR_REGION); } return cqEvent; } /** * Manages the CQs created for the base region. This is managed here, instead of on the base * region; since the cq could be created on the base region, before base region is created (using * newCq()). */ public void addToBaseRegionToCqNameMap(String regionName, String cqName) { synchronized (this.baseRegionToCqNameMap) { ArrayList<String> cqs = this.baseRegionToCqNameMap.get(regionName); if (cqs == null) { cqs = new ArrayList<String>(); } cqs.add(cqName); this.baseRegionToCqNameMap.put(regionName, cqs); } } public void removeFromBaseRegionToCqNameMap(String regionName, String cqName) { synchronized (this.baseRegionToCqNameMap) { ArrayList<String> cqs = this.baseRegionToCqNameMap.get(regionName); if (cqs != null) { cqs.remove(cqName); if (cqs.isEmpty()) { this.baseRegionToCqNameMap.remove(regionName); } else { this.baseRegionToCqNameMap.put(regionName, cqs); } } } } /** * Get the VSD ststs for CQ Service. There is one CQ Service per cache * * @return reference to VSD stats object for the CQ service */ public CqServiceVsdStats getCqServiceVsdStats() { return stats; } /** * Removes this CQ from CQ event Cache map. This disables the caching events for this CQ. * * @param cqName */ /* * synchronized public void removeCQFromCaching(String cqName){ if (cqToCqEventKeysMap != null) { * // Take a lock on CqQuery object. In processEvents the maps are // handled under CqQuery * object. if (cqToCqEventKeysMap != null){ synchronized (cqToCqEventKeysMap) { * cqToCqEventKeysMap.remove(cqName); } } } } */ /** * Returns the CQ event cache map. * * @return HashMap cqToCqEventKeysMap * * Caller must synchronize on the returned value in order to inspect. */ /* * public HashMap getCqToCqEventKeysMap(){ return cqToCqEventKeysMap; } */ /** * Adds the query from the given CQ to the matched CQ map. * * @param cq */ public void addToMatchingCqMap(CqQueryImpl cq) { synchronized (this.matchingCqMap) { String cqQuery = cq.getQueryString(); Set<String> matchingCQs = null; if (!matchingCqMap.containsKey(cqQuery)) { matchingCQs = Collections.newSetFromMap(new ConcurrentHashMap()); matchingCqMap.put(cqQuery, matchingCQs); this.stats.incUniqueCqQuery(); } else { matchingCQs = (Set) matchingCqMap.get(cqQuery); } matchingCQs.add(cq.getServerCqName()); if (logger.isDebugEnabled()) { logger.debug("Adding CQ into MatchingCQ map, CQName: {} Number of matched querys are: {}", cq.getServerCqName(), matchingCQs.size()); } } } /** * Removes the query from the given CQ from the matched CQ map. * * @param cq */ public void removeFromMatchingCqMap(CqQueryImpl cq) { synchronized (this.matchingCqMap) { String cqQuery = cq.getQueryString(); if (matchingCqMap.containsKey(cqQuery)) { Set matchingCQs = (Set) matchingCqMap.get(cqQuery); matchingCQs.remove(cq.getServerCqName()); if (logger.isDebugEnabled()) { logger.debug( "Removing CQ from MatchingCQ map, CQName: {} Number of matched querys are: {}", cq.getServerCqName(), matchingCQs.size()); } if (matchingCQs.isEmpty()) { matchingCqMap.remove(cqQuery); this.stats.decUniqueCqQuery(); } } } } /** * Returns the matching CQ map. * * @return HashMap matchingCqMap */ public Map<String, HashSet<String>> getMatchingCqMap() { return matchingCqMap; } /** * Applies the query on the event. This method takes care of the performance related changed done * to improve the CQ-query performance. When CQ-query is executed first time, it saves the query * related information in the execution context and uses that info in later executions. * * @param cQuery * @param event * @return boolean */ private boolean evaluateQuery(CqQueryImpl cQuery, Object[] event) throws Exception { ExecutionContext execContext = cQuery.getQueryExecutionContext(); execContext.reset(); execContext.setBindArguments(event); boolean status = false; // Check if the CQ query is executed once. // If not execute the query in normal way. // During this phase the query execution related info are stored in the // ExecutionContext. if (execContext.getScopeNum() <= 0) { SelectResults results = (SelectResults) ((DefaultQuery) cQuery.getQuery()).executeUsingContext(execContext); if (results != null && results.size() > 0) { status = true; } } else { // Execute using the saved query info (in ExecutionContext). // This avoids building resultSet, index look-up, generating build-plans // that are not required for; query execution on single object. CompiledSelect cs = ((DefaultQuery) (cQuery.getQuery())).getSelect(); status = cs.evaluateCq(execContext); } return status; } @Override public UserAttributes getUserAttributes(String cqName) { return this.cqNameToUserAttributesMap.get(cqName); } // public static void memberLeft(String poolName) { // if (cqServiceSingleton != null && !cqServiceSingleton.isServer()) { // cqServiceSingleton.sendMemberDisconnectedMessageToCqs(poolName); // } // } // // public static void memberCrashed(String poolName) { // if (cqServiceSingleton != null && !cqServiceSingleton.isServer()) { // cqServiceSingleton.sendMemberDisconnectedMessageToCqs(poolName); // } // } // @Override public void cqsDisconnected(Pool pool) { invokeCqsConnected(pool, false); } @Override public void cqsConnected(Pool pool) { invokeCqsConnected(pool, true); } /** * Let cq listeners know that they are connected or disconnected */ private void invokeCqsConnected(Pool pool, boolean connected) { String poolName = pool.getName(); // Check to see if we are already connected/disconnected. // If state has not changed, do not invoke another connected/disconnected synchronized (cqPoolsConnected) { // don't repeatily send same connect/disconnect message to cq's on repeated fails of // RedundancySatisfier if (cqPoolsConnected.containsKey(poolName) && connected == cqPoolsConnected.get(poolName)) { return; } cqPoolsConnected.put(poolName, connected); Collection<? extends InternalCqQuery> cqs = this.getAllCqs(); String cqName = null; final boolean isDebugEnabled = logger.isDebugEnabled(); for (InternalCqQuery query : cqs) { try { if (query == null) { continue; } cqName = query.getName(); ClientCQImpl cQuery = (ClientCQImpl) this.getCq(cqName); // Check cq pool to determine if the pool matches, if not continue. // Also if the connected state is already the same, we do not have to send status again. if (cQuery == null || cQuery.getCQProxy() == null) { continue; } Pool cqPool = cQuery.getCQProxy().getPool(); if (cQuery.isConnected() == connected || !cqPool.getName().equals(poolName)) { continue; } if ((!cQuery.isRunning() && cQuery.getQueuedEvents() == null)) { if (isDebugEnabled) { logger.debug("Unable to invoke CqListener, {}, CqName : {}", ((cQuery == null) ? "CQ not found" : " CQ is Not running"), cqName); } continue; } this.invokeCqConnectedListeners(cqName, cQuery, connected); } catch (VirtualMachineError e) { SystemFailure.initiateFailure(e); throw e; } catch (Throwable t) { SystemFailure.checkFailure(); logger.warn(LocalizedMessage .create(LocalizedStrings.CqService_ERROR_SENDING_CQ_CONNECTION_STATUS, cqName), t); if (t instanceof VirtualMachineError) { logger.warn(LocalizedMessage.create( LocalizedStrings.CqService_VIRTUALMACHINEERROR_PROCESSING_CQLISTENER_FOR_CQ_0, cqName), t); return; } } } } } @Override public List<String> getAllDurableCqsFromServer(InternalPool pool) { return new ServerCQProxyImpl(pool).getAllDurableCqsFromServer(); } }