001/*
002 * Copyright 2015 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.modeshape.observer.eventmappings;
017
018import static org.fcrepo.kernel.modeshape.utils.UncheckedFunction.uncheck;
019import static org.fcrepo.kernel.modeshape.observer.FedoraEventImpl.from;
020import static org.modeshape.jcr.api.JcrConstants.JCR_CONTENT;
021import static org.slf4j.LoggerFactory.getLogger;
022import static java.util.Arrays.asList;
023import static java.util.stream.Collectors.groupingBy;
024import static java.util.stream.Stream.empty;
025import static java.util.stream.Stream.of;
026import static javax.jcr.observation.Event.PROPERTY_ADDED;
027import static javax.jcr.observation.Event.PROPERTY_CHANGED;
028import static javax.jcr.observation.Event.PROPERTY_REMOVED;
029
030import java.util.List;
031import java.util.function.Function;
032import java.util.stream.Stream;
033
034import javax.jcr.RepositoryException;
035import javax.jcr.observation.Event;
036
037import org.fcrepo.kernel.api.exception.RepositoryRuntimeException;
038import org.fcrepo.kernel.api.observer.FedoraEvent;
039import org.fcrepo.kernel.modeshape.observer.FedoraEventImpl;
040
041import org.slf4j.Logger;
042
043/**
044 * Maps all JCR {@link Event}s concerning one JCR node to one {@link FedoraEvent}. Adds the types of those JCR events
045 * together to calculate the final type of the emitted FedoraEvent.
046 *
047 * @author ajs6f
048 * @author acoburn
049 * @since Feb 27, 2014
050 */
051public class AllNodeEventsOneEvent implements InternalExternalEventMapper {
052
053    private static final List<Integer> PROPERTY_EVENT_TYPES = asList(PROPERTY_ADDED, PROPERTY_CHANGED,
054            PROPERTY_REMOVED);
055
056    private final static Logger LOGGER = getLogger(AllNodeEventsOneEvent.class);
057
058    /**
059     * Extracts an identifier from a JCR {@link Event} by building an id from nodepath and user to collapse multiple
060     * events from repository mutations
061     */
062    private static final Function<Event, String> EXTRACT_NODE_ID = uncheck(ev -> {
063            final FedoraEvent event = from(ev);
064            final String id = event.getPath() + "-" + event.getUserID();
065            LOGGER.debug("Sorting an event by identifier: {}", id);
066            return id;
067    });
068
069    @Override
070    public Stream<FedoraEvent> apply(final Stream<Event> events) {
071        // first, index all the events by path-userID and then flatMap over that list of values
072        // each of which returns either a singleton Stream or an empty Stream. The final result
073        // will be a concatenated Stream of FedoraEvent objects.
074        return events.collect(groupingBy(EXTRACT_NODE_ID)).entrySet().stream().flatMap(entry -> {
075            final List<Event> evts = entry.getValue();
076            if (!evts.isEmpty()) {
077                // build a FedoraEvent from the first JCR Event
078                final FedoraEvent fedoraEvent = from(evts.get(0));
079                evts.stream().forEach(evt -> {
080                    // add types to the FedoraEvent from the JCR Event
081                    fedoraEvent.addType(FedoraEventImpl.valueOf(evt.getType()));
082                    try {
083                        // add appropriate properties to the FedoraEvent from the JCR Event
084                        if (evt.getPath().contains(JCR_CONTENT)) {
085                            fedoraEvent.addProperty("fedora:hasContent");
086                        }
087                        if (PROPERTY_EVENT_TYPES.contains(evt.getType())) {
088                            final String eventPath = evt.getPath();
089                            fedoraEvent.addProperty(eventPath.substring(eventPath.lastIndexOf('/') + 1));
090                        } else {
091                            LOGGER.trace("Not adding non-event property: {}, {}", fedoraEvent, evt);
092                        }
093                    } catch (final RepositoryException ex) {
094                        throw new RepositoryRuntimeException(ex);
095                    }
096                });
097                return of(fedoraEvent);
098            }
099            return empty();
100        });
101    }
102}