001/** 002 * Copyright 2014 DuraSpace, Inc. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.fcrepo.kernel.impl.observer.eventmappings; 017 018import static com.google.common.base.Throwables.propagate; 019import static com.google.common.collect.Multimaps.index; 020import static org.slf4j.LoggerFactory.getLogger; 021import static javax.jcr.observation.Event.PROPERTY_ADDED; 022import static javax.jcr.observation.Event.PROPERTY_CHANGED; 023import static javax.jcr.observation.Event.PROPERTY_REMOVED; 024 025import java.util.Iterator; 026 027import javax.jcr.RepositoryException; 028import javax.jcr.observation.Event; 029 030import org.fcrepo.kernel.observer.FedoraEvent; 031import org.fcrepo.kernel.observer.eventmappings.InternalExternalEventMapper; 032import org.slf4j.Logger; 033import com.google.common.base.Function; 034import com.google.common.collect.Multimap; 035 036/** 037 * Maps all JCR {@link Event}s concerning one JCR node to one 038 * {@link FedoraEvent}. Adds the types of those JCR events together to calculate 039 * the final type of the emitted FedoraEvent. TODO stop aggregating events in 040 * the heap and make this a purely iterative algorithm, if possible 041 * 042 * @author ajs6f 043 * @since Feb 27, 2014 044 */ 045public class AllNodeEventsOneEvent implements InternalExternalEventMapper { 046 047 /** 048 * Simply extracts the node identifier from a JCR {@link Event}. 049 */ 050 private static final Function<Event, String> EXTRACT_NODE_ID = new Function<Event, String>() { 051 052 @Override 053 public String apply(final Event ev) { 054 try { 055 final String id = ev.getIdentifier(); 056 log.debug("Sorting an event by identifier: {}", id); 057 return id; 058 } catch (final RepositoryException e) { 059 throw propagate(e); 060 } 061 } 062 }; 063 064 @Override 065 public Iterator<FedoraEvent> apply(final Iterator<Event> events) { 066 067 return new Iterator<FedoraEvent>() { 068 069 // sort JCR events into a Multimap keyed on the node ID involved 070 final Multimap<String, Event> sortedEvents = index(events, EXTRACT_NODE_ID); 071 072 final Iterator<String> nodeIds = sortedEvents.keySet().iterator(); 073 074 @Override 075 public boolean hasNext() { 076 return nodeIds.hasNext(); 077 } 078 079 @Override 080 public FedoraEvent next() { 081 final Iterator<Event> nodeSpecificEvents = sortedEvents.get(nodeIds.next()).iterator(); 082 // we can safely call next() immediately on nodeSpecificEvents 083 // because if 084 // there was no event at all, there would appear no entry in our 085 // Multimap under this key 086 final Event firstEvent = nodeSpecificEvents.next(); 087 final FedoraEvent fedoraEvent = new FedoraEvent(firstEvent); 088 089 try { 090 addProperty(fedoraEvent, firstEvent); 091 while (nodeSpecificEvents.hasNext()) { 092 // add the event type and property name to the event we are building up to emit 093 // we could aggregate other information here if that seems useful 094 final Event otherEvent = nodeSpecificEvents.next(); 095 fedoraEvent.addType(otherEvent.getType()); 096 addProperty(fedoraEvent, otherEvent); 097 } 098 099 } catch (Exception ex) { 100 log.warn("Danger: swallowing exception", ex); 101 } 102 return fedoraEvent; 103 } 104 105 @Override 106 public void remove() { 107 // the underlying Multimap is immutable anyway 108 throw new UnsupportedOperationException(); 109 } 110 111 private void addProperty( final FedoraEvent fedoraEvent, final Event e ) { 112 try { 113 if ( e.getType() == PROPERTY_ADDED || 114 e.getType() == PROPERTY_CHANGED || 115 e.getType() == PROPERTY_REMOVED ) { 116 fedoraEvent.addProperty( e.getPath().substring(e.getPath().lastIndexOf("/") + 1) ); 117 118 } else { 119 log.trace("Not adding non-event property: {}, {}", fedoraEvent, e); 120 } 121 } catch (final RepositoryException ex) { 122 throw propagate(ex); 123 } 124 } 125 }; 126 } 127 128 private final static Logger log = getLogger(AllNodeEventsOneEvent.class); 129}