001/* 002 * Licensed to DuraSpace under one or more contributor license agreements. 003 * See the NOTICE file distributed with this work for additional information 004 * regarding copyright ownership. 005 * 006 * DuraSpace licenses this file to you under the Apache License, 007 * Version 2.0 (the "License"); you may not use this file except in 008 * compliance with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.fcrepo.kernel.modeshape.observer.eventmappings; 019 020import static org.fcrepo.kernel.modeshape.FedoraJcrConstants.FEDORA_JCR_REFERENCE; 021import static org.fcrepo.kernel.modeshape.FedoraJcrConstants.JCR_LASTMODIFIED; 022import static org.fcrepo.kernel.modeshape.utils.UncheckedFunction.uncheck; 023import static org.fcrepo.kernel.modeshape.observer.FedoraEventImpl.from; 024import static org.fcrepo.kernel.modeshape.observer.FedoraEventImpl.getResourceTypes; 025import static org.slf4j.LoggerFactory.getLogger; 026import static java.util.stream.Collectors.groupingBy; 027import static java.util.stream.Collectors.toSet; 028import static java.util.stream.Stream.empty; 029import static java.util.stream.Stream.of; 030import static javax.jcr.observation.Event.PROPERTY_CHANGED; 031 032import java.util.ArrayList; 033import java.util.List; 034import java.util.function.Function; 035import java.util.stream.Stream; 036 037import javax.jcr.RepositoryException; 038import javax.jcr.observation.Event; 039 040import org.fcrepo.kernel.api.exception.RepositoryRuntimeException; 041import org.fcrepo.kernel.api.observer.FedoraEvent; 042import org.fcrepo.kernel.modeshape.observer.FedoraEventImpl; 043import org.fcrepo.kernel.modeshape.observer.WrappedJcrEvent; 044 045import org.slf4j.Logger; 046 047/** 048 * Maps all JCR {@link Event}s concerning one JCR node to one {@link FedoraEvent}. Adds the types of those JCR events 049 * together to calculate the final type of the emitted FedoraEvent. 050 * 051 * @author ajs6f 052 * @author acoburn 053 * @since Feb 27, 2014 054 */ 055public class AllNodeEventsOneEvent implements InternalExternalEventMapper { 056 057 private final static Logger LOGGER = getLogger(AllNodeEventsOneEvent.class); 058 059 /** 060 * If the only event is a modification of the jcr:lastModified it is most likely the effect of an inbound reference. 061 * Wrap the old event in a new one that replaces the event type with FEDORA_JCR_REFERENCE for later mapping. 062 * 063 * @param ev The list of events to check. 064 * @return The original list or a list with the altered event. 065 */ 066 private static List<Event> AlterReferenceEvents(final List<Event> ev) { 067 try { 068 if (ev.size() == 1 && ev.get(0).getPath().endsWith("/" + JCR_LASTMODIFIED) && 069 ev.get(0).getType() == PROPERTY_CHANGED) { 070 final Event original = ev.get(0); 071 final Event tempEv = 072 new WrappedJcrEvent((org.modeshape.jcr.api.observation.Event) original, FEDORA_JCR_REFERENCE); 073 final List<Event> list = new ArrayList<>(); 074 list.add(tempEv); 075 return list; 076 } 077 return ev; 078 } catch (final RepositoryException e) { 079 throw new RepositoryRuntimeException(e); 080 } 081 } 082 083 /** 084 * Extracts an identifier from a JCR {@link Event} by building an id from nodepath and user to collapse multiple 085 * events from repository mutations 086 */ 087 private static final Function<Event, String> EXTRACT_NODE_ID = uncheck(ev -> { 088 final FedoraEvent event = from(ev); 089 final String id = event.getPath() + "-" + event.getUserID(); 090 LOGGER.debug("Sorting an event by identifier: {}", id); 091 return id; 092 }); 093 094 @Override 095 public Stream<FedoraEvent> apply(final Stream<Event> events) { 096 // first, index all the events by path-userID and then flatMap over that list of values 097 // each of which returns either a singleton Stream or an empty Stream. The final result 098 // will be a concatenated Stream of FedoraEvent objects. 099 return events.collect(groupingBy(EXTRACT_NODE_ID)).entrySet().stream().flatMap(entry -> { 100 final List<Event> evts = AlterReferenceEvents(entry.getValue()); 101 if (!evts.isEmpty()) { 102 // build a FedoraEvent from the first JCR Event 103 final FedoraEvent fedoraEvent = from(evts.get(0)); 104 evts.stream().skip(1).forEach(evt -> { 105 // add types to the FedoraEvent from the subsequent JCR Events 106 fedoraEvent.getTypes().add(FedoraEventImpl.valueOf(evt.getType())); 107 fedoraEvent.getResourceTypes().addAll(getResourceTypes(evt).collect(toSet())); 108 }); 109 return of(fedoraEvent); 110 } 111 return empty(); 112 }); 113 } 114}