/*
* 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.jackrabbit.core.observation;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.jcr.RepositoryException;
import javax.jcr.nodetype.NodeType;
import javax.jcr.observation.Event;
import org.apache.jackrabbit.core.SessionImpl;
import org.apache.jackrabbit.core.id.NodeId;
import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
import org.apache.jackrabbit.spi.Path;
/**
* The <code>EventFilter</code> class implements the filter logic based
* on the session's access rights and the specified filter rules.
*/
public class EventFilter {
static final EventFilter BLOCK_ALL = new BlockAllFilter();
/**
* The session this EventFilter belongs to.
*/
private final SessionImpl session;
/**
* This <code>EventFilter</code> should only allow events with the
* specified types.
*/
private final long eventTypes;
/**
* Only allow Items with the specified <code>paths</code>
*/
private final List<Path> paths;
/**
* If <code>isDeep</code> is <code>true</code> also Items under <code>absPath</code>
* are allowed.
*/
private final boolean isDeep;
/**
* Only allow Nodes with the specified <code>uuids</code>.
*/
private final NodeId[] ids;
/**
* Only allow Nodes with the specified {@link javax.jcr.nodetype.NodeType}s.
*/
private final NodeTypeImpl[] nodeTypes;
/**
* If <code>noLocal</code> is true this filter will block events from
* the session that registered this filter.
*/
private final boolean noLocal;
/**
* If <code>noExternal</code> is true this filter will block events from
* other cluster nodes.
*/
private final boolean noExternal;
/**
* If <code>noInternal</code> is true this filter will block events from
* this cluster nodes.
*/
private final boolean noInternal;
/**
* Creates a new <code>EventFilter</code> instance.
*
* @param session the <code>Session</code> that registered the {@link
* javax.jcr.observation.EventListener}.
* @param eventTypes only allow specified {@link javax.jcr.observation.Event} types.
* @param paths only allow {@link javax.jcr.Item} with a path in
* <code>paths</code>.
* @param isDeep if <code>true</code> also allow events for {@link
* javax.jcr.Item}s below <code>absPath</code>.
* @param ids only allow events for {@link javax.jcr.Node}s with
* specified NodeIDs. If <code>null</code> is passed no
* restriction regarding NodeIds is applied.
* @param nodeTypes only allow events for specified {@link
* javax.jcr.nodetype.NodeType}s. If <code>null</code> no
* node type restriction is applied.
* @param noLocal if <code>true</code> no events are allowed that were
* created from changes related to the <code>Session</code>
* that registered the {@link javax.jcr.observation.EventListener}.
* @param noExternal if <code>true</code> no events are allowed that were
* created from changes on an external cluster node.
* @param noInternal if <code>true</code> no events are allowed that were
* created from changes on the local cluster node.
*/
EventFilter(SessionImpl session,
long eventTypes,
List<Path> paths,
boolean isDeep,
NodeId[] ids,
NodeTypeImpl[] nodeTypes,
boolean noLocal,
boolean noExternal,
boolean noInternal) {
this.session = session;
this.eventTypes = eventTypes;
this.paths = paths;
this.isDeep = isDeep;
this.ids = ids;
this.noLocal = noLocal;
this.noExternal = noExternal;
this.noInternal = noInternal;
this.nodeTypes = nodeTypes;
}
/**
* Returns <code>true</code> if this <code>EventFilter</code> does not allow
* the specified <code>EventState</code>; <code>false</code> otherwise.
*
* @param eventState the <code>EventState</code> in question.
* @return <code>true</code> if this <code>EventFilter</code> blocks the
* <code>EventState</code>.
* @throws RepositoryException if an error occurs while checking.
*/
boolean blocks(EventState eventState) throws RepositoryException {
// first do cheap checks
// check event type
long type = eventState.getType();
if ((eventTypes & type) == 0) {
return true;
}
// check for session local changes
if (noLocal && session.equals(eventState.getSession())) {
// listener does not wish to get local events
return true;
}
if (noExternal && eventState.isExternal()) {
return true;
}
if (noInternal && !eventState.isExternal()) {
return true;
}
// UUIDs, types, and paths do not need to match for persist
if (eventState.getType() == Event.PERSIST) {
return false;
}
// check UUIDs
NodeId parentId = eventState.getParentId();
if (ids != null) {
boolean match = false;
for (int i = 0; i < ids.length && !match; i++) {
match |= parentId.equals(ids[i]);
}
if (!match) {
return true;
}
}
// check node types
if (nodeTypes != null) {
Set<NodeType> eventTypes = eventState.getNodeTypes(session.getNodeTypeManager());
boolean match = false;
for (int i = 0; i < nodeTypes.length && !match; i++) {
for (NodeType eventType : eventTypes) {
NodeTypeImpl nodeType = (NodeTypeImpl) eventType;
match |= nodeType.getQName().equals(nodeTypes[i].getQName())
|| nodeType.isDerivedFrom(nodeTypes[i].getQName());
}
}
if (!match) {
return true;
}
}
// finally check paths
Path eventPath = eventState.getParentPath();
boolean match = false;
for (Path path : paths) {
if (eventPath.equals(path) || isDeep && eventPath.isDescendantOf(path)) {
match = true;
break;
}
}
return !match;
}
/**
* This class implements an <code>EventFilter</code> that blocks
* all {@link EventState}s.
*/
private static final class BlockAllFilter extends EventFilter {
/**
* Creates a new <code>BlockAllFilter</code>.
*/
BlockAllFilter() {
super(null, 0, Collections.<Path>emptyList(), true, null, null, true, true, true);
}
/**
* Always return <code>true</code>.
*
* @return always <code>true</code>.
*/
@Override
boolean blocks(EventState eventState) {
return true;
}
}
}