/*
* ModeShape (http://www.modeshape.org)
*
* Licensed 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.modeshape.jcr;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.jcr.RepositoryException;
import javax.jcr.nodetype.NodeType;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;
import org.modeshape.common.logging.Logger;
import org.modeshape.common.util.StringUtil;
/**
* Test implementation of an {@link javax.jcr.observation.EventListener}
*/
public class SimpleListener implements EventListener {
private static final Logger LOGGER = Logger.getLogger(SimpleListener.class);
private String errorMessage;
protected final List<Event> events;
protected final List<String> userData;
private int eventsProcessed = 0;
protected final int eventTypes;
protected final int expectedEventsCount;
protected String expectedNodePrimaryType;
protected TreeSet<String> expectedNodeMixinTypes;
protected final CountDownLatch latch;
public SimpleListener( int expectedEventsCount,
int numIterators,
int eventTypes ) {
this.eventTypes = eventTypes;
this.expectedEventsCount = expectedEventsCount;
this.events = new ArrayList<Event>();
this.userData = new ArrayList<String>();
this.latch = new CountDownLatch(numIterators);
}
public int getActualEventCount() {
return this.eventsProcessed;
}
public String getErrorMessage() {
return this.errorMessage;
}
public List<Event> getEvents() {
return this.events;
}
public int getExpectedEventCount() {
return this.expectedEventsCount;
}
public SimpleListener withExpectedNodePrimaryType(String nodePrimaryType) {
this.expectedNodePrimaryType = nodePrimaryType;
return this;
}
public SimpleListener withExpectedNodeMixinTypes(String... mixinTypes) {
this.expectedNodeMixinTypes = new TreeSet<String>(Arrays.asList(mixinTypes));
return this;
}
@Override
public void onEvent( EventIterator itr ) {
// this is called each time a "transaction" is committed. Most times this means after a session.save. But there are
// other times, like a workspace.move and a node.lock
try {
long position = itr.getPosition();
// iterator position must be set initially zero
if (position == 0) {
while (itr.hasNext()) {
org.modeshape.jcr.api.observation.Event event = (org.modeshape.jcr.api.observation.Event)itr.nextEvent();
// System.out.println(event + " from " + this);
// check iterator position
if (++position != itr.getPosition()) {
this.errorMessage = "EventIterator position was " + itr.getPosition() + " and should be " + position;
break;
}
try {
String userData = event.getUserData();
this.userData.add(userData);
} catch (RepositoryException e) {
LOGGER.debug(e, "Listener exception");
this.errorMessage = e.getMessage();
}
// add event to collection and increment total
this.events.add(event);
++this.eventsProcessed;
// check to make sure we haven't received too many events
if (this.eventsProcessed > this.expectedEventsCount) {
break;
}
// check event type
int eventType = event.getType();
if ((this.eventTypes & eventType) == 0) {
this.errorMessage = "Received a wrong event type of " + eventType;
break;
}
if (!StringUtil.isBlank(expectedNodePrimaryType)) {
try {
String actualNodeType = event.getPrimaryNodeType().getName();
if (!actualNodeType.equalsIgnoreCase(expectedNodePrimaryType)) {
this.errorMessage = "Incorrect node primary type. Expected " + expectedNodePrimaryType + " but received " + actualNodeType;
break;
}
} catch (RepositoryException e) {
LOGGER.debug(e, "Listener exception");
this.errorMessage = e.getMessage();
break;
}
}
if (expectedNodeMixinTypes != null) {
try {
Set<String> actualNodeMixins = new TreeSet<String>();
for (NodeType mixin : event.getMixinNodeTypes()) {
actualNodeMixins.add(mixin.getName());
}
if (!expectedNodeMixinTypes.equals(actualNodeMixins)) {
this.errorMessage = "Incorrect node mixins. Expected " + expectedNodeMixinTypes + " but received " + actualNodeMixins;
}
} catch (RepositoryException e) {
LOGGER.debug(e, "Listener exception");
this.errorMessage = e.getMessage();
break;
}
}
}
} else {
this.errorMessage = "EventIterator position was not initially set to zero";
}
} finally {
// This has to be done LAST, otherwise waitForEvents() will return before the above stuff is done
this.latch.countDown();
}
}
public void waitForEvents() throws Exception {
long millis = this.expectedEventsCount == 0 ? 50 : 500;
this.latch.await(millis, TimeUnit.MILLISECONDS);
}
}