/*
* 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.cq.dunit;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import org.apache.geode.LogWriter;
import org.apache.geode.cache.Operation;
import org.apache.geode.cache.client.Pool;
import org.apache.geode.cache.query.CqEvent;
import org.apache.geode.cache.query.CqStatusListener;
import org.apache.geode.distributed.internal.InternalDistributedSystem;
import org.apache.geode.test.dunit.Wait;
import org.apache.geode.test.dunit.WaitCriterion;
import com.jayway.awaitility.Awaitility;
/**
*
*/
public class CqQueryTestListener implements CqStatusListener {
protected final LogWriter logger;
protected volatile int eventCreateCount = 0;
protected volatile int eventUpdateCount = 0;
protected volatile int eventDeleteCount = 0;
protected volatile int eventInvalidateCount = 0;
protected volatile int eventErrorCount = 0;
protected volatile int totalEventCount = 0;
protected volatile int eventQueryInsertCount = 0;
protected volatile int eventQueryUpdateCount = 0;
protected volatile int eventQueryDeleteCount = 0;
protected volatile int eventQueryInvalidateCount = 0;
protected volatile int cqsConnectedCount = 0;
protected volatile int cqsDisconnectedCount = 0;
protected volatile boolean eventClose = false;
protected volatile boolean eventRegionClear = false;
protected volatile boolean eventRegionInvalidate = false;
final public Set destroys = Collections.synchronizedSet(new HashSet());
final public Set creates = Collections.synchronizedSet(new HashSet());
final public Set invalidates = Collections.synchronizedSet(new HashSet());
final public Set updates = Collections.synchronizedSet(new HashSet());
final public Set errors = Collections.synchronizedSet(new HashSet());
static private final String WAIT_PROPERTY = "CqQueryTestListener.maxWaitTime";
static private final int WAIT_DEFAULT = (20 * 1000);
public static final long MAX_TIME = Integer.getInteger(WAIT_PROPERTY, WAIT_DEFAULT).intValue();;
public String cqName;
public String userName;
// This is to avoid reference to PerUserRequestSecurityTest which will fail to
// initialize in a non Hydra environment.
static public boolean usedForUnitTests = true;
public ConcurrentLinkedQueue events = new ConcurrentLinkedQueue();
public ConcurrentLinkedQueue cqEvents = new ConcurrentLinkedQueue();
public CqQueryTestListener(LogWriter logger) {
this.logger = logger;
}
public void onEvent(CqEvent cqEvent) {
this.totalEventCount++;
Operation baseOperation = cqEvent.getBaseOperation();
Operation queryOperation = cqEvent.getQueryOperation();
Object key = cqEvent.getKey();
// logger.info("CqEvent for the CQ: " + this.cqName +
// "; Key=" + key +
// "; baseOp=" + baseOperation +
// "; queryOp=" + queryOperation +
// "; totalEventCount=" + this.totalEventCount
// );
if (key != null) {
events.add(key);
cqEvents.add(cqEvent);
}
if (baseOperation.isUpdate()) {
this.eventUpdateCount++;
this.updates.add(key);
} else if (baseOperation.isCreate()) {
this.eventCreateCount++;
this.creates.add(key);
} else if (baseOperation.isDestroy()) {
this.eventDeleteCount++;
this.destroys.add(key);
} else if (baseOperation.isInvalidate()) {
this.eventDeleteCount++;
this.invalidates.add(key);
}
if (queryOperation.isUpdate()) {
this.eventQueryUpdateCount++;
} else if (queryOperation.isCreate()) {
this.eventQueryInsertCount++;
} else if (queryOperation.isDestroy()) {
this.eventQueryDeleteCount++;
} else if (queryOperation.isInvalidate()) {
this.eventQueryInvalidateCount++;
} else if (queryOperation.isClear()) {
this.eventRegionClear = true;
} else if (queryOperation.isRegionInvalidate()) {
this.eventRegionInvalidate = true;
}
}
public void onError(CqEvent cqEvent) {
this.eventErrorCount++;
this.errors.add(cqEvent.getThrowable().getMessage());
}
public void onCqDisconnected() {
this.cqsDisconnectedCount++;
}
public void onCqConnected() {
this.cqsConnectedCount++;
}
public int getErrorEventCount() {
return this.eventErrorCount;
}
public int getTotalEventCount() {
return this.totalEventCount;
}
public int getCreateEventCount() {
return this.eventCreateCount;
}
public int getUpdateEventCount() {
return this.eventUpdateCount;
}
public int getDeleteEventCount() {
return this.eventDeleteCount;
}
public int getInvalidateEventCount() {
return this.eventInvalidateCount;
}
public int getQueryInsertEventCount() {
return this.eventQueryInsertCount;
}
public int getQueryUpdateEventCount() {
return this.eventQueryUpdateCount;
}
public int getQueryDeleteEventCount() {
return this.eventQueryDeleteCount;
}
public int getQueryInvalidateEventCount() {
return this.eventQueryInvalidateCount;
}
public Object[] getEvents() {
return this.cqEvents.toArray();
}
public void close() {
this.eventClose = true;
}
public void printInfo(final boolean printKeys) {
logger.info("####" + this.cqName + ": " + " Events Total :" + this.getTotalEventCount()
+ " Events Created :" + this.eventCreateCount + " Events Updated :" + this.eventUpdateCount
+ " Events Deleted :" + this.eventDeleteCount + " Events Invalidated :"
+ this.eventInvalidateCount + " Query Inserts :" + this.eventQueryInsertCount
+ " Query Updates :" + this.eventQueryUpdateCount + " Query Deletes :"
+ this.eventQueryDeleteCount + " Query Invalidates :" + this.eventQueryInvalidateCount
+ " Total Events :" + this.totalEventCount);
if (printKeys) {
// for debugging on failuers ...
logger.info("Number of Insert for key : " + this.creates.size() + " and updates : "
+ this.updates.size() + " and number of destroys : " + this.destroys.size()
+ " and number of invalidates : " + this.invalidates.size());
logger.info("Keys in created sets : " + this.creates.toString());
logger.info("Key in updates sets : " + this.updates.toString());
logger.info("Key in destorys sets : " + this.destroys.toString());
logger.info("Key in invalidates sets : " + this.invalidates.toString());
}
}
public boolean waitForCreated(final Object key) {
WaitCriterion ev = new WaitCriterion() {
public boolean done() {
return CqQueryTestListener.this.creates.contains(key);
}
public String description() {
return "never got create event for CQ " + CqQueryTestListener.this.cqName + " key " + key;
}
};
Wait.waitForCriterion(ev, MAX_TIME, 200, true);
return true;
}
public boolean waitForTotalEvents(final int total) {
WaitCriterion ev = new WaitCriterion() {
public boolean done() {
return (CqQueryTestListener.this.totalEventCount == total);
}
public String description() {
return "Did not receive expected number of events " + CqQueryTestListener.this.cqName
+ " expected: " + total + " receieved: " + CqQueryTestListener.this.totalEventCount;
}
};
Wait.waitForCriterion(ev, MAX_TIME, 200, true);
return true;
}
public boolean waitForDestroyed(final Object key) {
WaitCriterion ev = new WaitCriterion() {
public boolean done() {
return CqQueryTestListener.this.destroys.contains(key);
}
public String description() {
return "never got destroy event for key " + key + " in CQ "
+ CqQueryTestListener.this.cqName;
}
};
Wait.waitForCriterion(ev, MAX_TIME, 200, true);
return true;
}
public boolean waitForInvalidated(final Object key) {
WaitCriterion ev = new WaitCriterion() {
public boolean done() {
return CqQueryTestListener.this.invalidates.contains(key);
}
public String description() {
return "never got invalidate event for CQ " + CqQueryTestListener.this.cqName;
}
};
Wait.waitForCriterion(ev, MAX_TIME, 200, true);
return true;
}
public boolean waitForUpdated(final Object key) {
WaitCriterion ev = new WaitCriterion() {
public boolean done() {
return CqQueryTestListener.this.updates.contains(key);
}
public String description() {
return "never got update event for CQ " + CqQueryTestListener.this.cqName;
}
};
Wait.waitForCriterion(ev, MAX_TIME, 200, true);
return true;
}
public boolean waitForClose() {
WaitCriterion ev = new WaitCriterion() {
public boolean done() {
return CqQueryTestListener.this.eventClose;
}
public String description() {
return "never got close event for CQ " + CqQueryTestListener.this.cqName;
}
};
Wait.waitForCriterion(ev, MAX_TIME, 200, true);
return true;
}
public boolean waitForRegionClear() {
WaitCriterion ev = new WaitCriterion() {
public boolean done() {
return CqQueryTestListener.this.eventRegionClear;
}
public String description() {
return "never got region clear event for CQ " + CqQueryTestListener.this.cqName;
}
};
Wait.waitForCriterion(ev, MAX_TIME, 200, true);
return true;
}
public boolean waitForRegionInvalidate() {
WaitCriterion ev = new WaitCriterion() {
public boolean done() {
return CqQueryTestListener.this.eventRegionInvalidate;
}
public String description() {
return "never got region invalidate event for CQ " + CqQueryTestListener.this.cqName;
}
};
Wait.waitForCriterion(ev, MAX_TIME, 200, true);
return true;
}
public boolean waitForError(final String expectedMessage) {
WaitCriterion ev = new WaitCriterion() {
public boolean done() {
Iterator iterator = CqQueryTestListener.this.errors.iterator();
while (iterator.hasNext()) {
String errorMessage = (String) iterator.next();
if (errorMessage.equals(expectedMessage)) {
return true;
} else {
logger.fine("errors that exist:" + errorMessage);
}
}
return false;
}
public String description() {
return "never got create error for CQ " + CqQueryTestListener.this.cqName + " messaged "
+ expectedMessage;
}
};
Wait.waitForCriterion(ev, MAX_TIME, 200, true);
return true;
}
public boolean waitForCqsDisconnectedEvents(final int total) {
WaitCriterion ev = new WaitCriterion() {
public boolean done() {
return (CqQueryTestListener.this.cqsDisconnectedCount == total);
}
public String description() {
return "Did not receive expected number of calls to cqsDisconnected() "
+ CqQueryTestListener.this.cqName + " expected: " + total + " received: "
+ CqQueryTestListener.this.cqsDisconnectedCount;
}
};
Wait.waitForCriterion(ev, MAX_TIME, 200, true);
return true;
}
public boolean waitForCqsConnectedEvents(final int total) {
WaitCriterion ev = new WaitCriterion() {
public boolean done() {
return (CqQueryTestListener.this.cqsConnectedCount == total);
}
public String description() {
return "Did not receive expected number of calls to cqsConnected() "
+ CqQueryTestListener.this.cqName + " expected: " + total + " receieved: "
+ CqQueryTestListener.this.cqsConnectedCount;
}
};
Wait.waitForCriterion(ev, MAX_TIME, 200, true);
return true;
}
public void waitForEvents(final int creates, final int updates, final int deletes,
final int queryInserts, final int queryUpdates, final int queryDeletes,
final int totalEvents) {
// Wait for expected events to arrive
try {
Awaitility.await().atMost(5, TimeUnit.SECONDS).until(() -> {
if ((creates > 0 && creates != this.getCreateEventCount())
|| (updates > 0 && updates != this.getUpdateEventCount())
|| (deletes > 0 && deletes != this.getDeleteEventCount())
|| (queryInserts > 0 && queryInserts != this.getQueryInsertEventCount())
|| (queryUpdates > 0 && queryUpdates != this.getQueryUpdateEventCount())
|| (queryDeletes > 0 && queryDeletes != this.getQueryDeleteEventCount())
|| (totalEvents > 0 && totalEvents != this.getTotalEventCount())) {
return false;
}
return true;
});
} catch (Exception ex) {
// We just wait for expected events to arrive.
// Caller will do validation and throw exception.
}
}
public void getEventHistory() {
destroys.clear();
creates.clear();
invalidates.clear();
updates.clear();
this.eventClose = false;
}
}