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.impl.observer;
017
018import static com.codahale.metrics.MetricRegistry.name;
019import static com.google.common.base.Throwables.propagate;
020import static com.google.common.collect.Iterators.filter;
021import static com.google.common.collect.Iterators.transform;
022import static javax.jcr.observation.Event.NODE_ADDED;
023import static javax.jcr.observation.Event.NODE_MOVED;
024import static javax.jcr.observation.Event.NODE_REMOVED;
025import static javax.jcr.observation.Event.PROPERTY_ADDED;
026import static javax.jcr.observation.Event.PROPERTY_CHANGED;
027import static javax.jcr.observation.Event.PROPERTY_REMOVED;
028import static org.slf4j.LoggerFactory.getLogger;
029import  org.fcrepo.metrics.RegistryService;
030
031import java.util.Iterator;
032
033import javax.annotation.PostConstruct;
034import javax.annotation.PreDestroy;
035import javax.inject.Inject;
036import javax.jcr.RepositoryException;
037import javax.jcr.Session;
038import javax.jcr.observation.Event;
039import javax.jcr.observation.EventListener;
040
041import org.fcrepo.kernel.observer.EventFilter;
042import org.fcrepo.kernel.observer.FedoraEvent;
043import org.fcrepo.kernel.observer.eventmappings.InternalExternalEventMapper;
044import org.modeshape.jcr.api.Repository;
045import org.slf4j.Logger;
046
047import com.codahale.metrics.Counter;
048import com.google.common.eventbus.EventBus;
049
050/**
051 * Simple JCR EventListener that filters JCR Events through a Fedora EventFilter
052 * and puts the resulting stream onto the internal Fedora EventBus as a stream
053 * of FedoraEvents.
054 *
055 * @author eddies
056 * @author ajs6f
057 * @since Feb 7, 2013
058 */
059public class SimpleObserver implements EventListener {
060
061    private static final Logger LOGGER = getLogger(SimpleObserver.class);
062
063    /**
064     * A simple counter of events that pass through this observer
065     */
066    static final Counter EVENT_COUNTER =
067            RegistryService.getInstance().getMetrics().counter(name(SimpleObserver.class, "onEvent"));
068
069    static final Integer EVENT_TYPES = NODE_ADDED + NODE_REMOVED + NODE_MOVED + PROPERTY_ADDED + PROPERTY_CHANGED
070            + PROPERTY_REMOVED;
071
072    @Inject
073    private Repository repository;
074
075    @Inject
076    private EventBus eventBus;
077
078    @Inject
079    private InternalExternalEventMapper eventMapper;
080
081    @Inject
082    private EventFilter eventFilter;
083
084    // THIS SESSION SHOULD NOT BE USED TO LOOK UP NODES
085    // it is used only to register and deregister this observer to the JCR
086    private Session session;
087
088    /**
089     * Register this observer with the JCR event listeners
090     *
091     * @throws RepositoryException if repository exception occurred
092     */
093    @PostConstruct
094    public void buildListener() throws RepositoryException {
095        LOGGER.debug("Constructing an observer for JCR events...");
096        session = repository.login();
097        session.getWorkspace().getObservationManager()
098                .addEventListener(this, EVENT_TYPES, "/", true, null, null, false);
099        session.save();
100    }
101
102    /**
103     * logout of the session
104     *
105     * @throws RepositoryException if repository exception occurred
106     */
107    @PreDestroy
108    public void stopListening() throws RepositoryException {
109        LOGGER.debug("Destroying an observer for JCR events...");
110        session.getWorkspace().getObservationManager().removeEventListener(this);
111        session.logout();
112    }
113
114    /**
115     * Filter JCR events and transform them into our own FedoraEvents.
116     *
117     * @param events the JCR events
118     */
119    @Override
120    public void onEvent(final javax.jcr.observation.EventIterator events) {
121        Session lookupSession = null;
122        try {
123            lookupSession = repository.login();
124
125            @SuppressWarnings("unchecked")
126            final Iterator<Event> filteredEvents = filter(events, eventFilter.getFilter(lookupSession));
127            final Iterator<FedoraEvent> publishableEvents = eventMapper.apply(filteredEvents);
128            final Iterator<FedoraEvent> namespacedEvents =
129                    transform(publishableEvents, new GetNamespacedProperties(lookupSession));
130
131            while (namespacedEvents.hasNext()) {
132                eventBus.post(namespacedEvents.next());
133                EVENT_COUNTER.inc();
134            }
135        } catch (final RepositoryException ex) {
136            throw propagate(ex);
137        } finally {
138            if (lookupSession != null) {
139                lookupSession.logout();
140            }
141        }
142    }
143}