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;
017
018import static com.google.common.base.MoreObjects.toStringHelper;
019import static com.google.common.base.Preconditions.checkArgument;
020import static com.google.common.collect.Sets.union;
021import static org.fcrepo.kernel.api.utils.EventType.valueOf;
022import static org.modeshape.jcr.api.JcrConstants.JCR_CONTENT;
023import static java.util.Collections.singleton;
024import static java.util.stream.Collectors.toList;
025import static javax.jcr.observation.Event.PROPERTY_ADDED;
026import static javax.jcr.observation.Event.PROPERTY_CHANGED;
027import static javax.jcr.observation.Event.PROPERTY_REMOVED;
028
029import java.util.Arrays;
030import java.util.HashSet;
031import java.util.List;
032import java.util.Set;
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.api.services.functions.HierarchicalIdentifierSupplier;
040import org.fcrepo.kernel.api.services.functions.UniqueValueSupplier;
041import org.fcrepo.kernel.api.utils.EventType;
042
043/**
044 * A very simple abstraction to prevent event-driven machinery downstream from the repository from relying directly
045 * on a JCR interface {@link Event}. Can represent either a single JCR event or several.
046 *
047 * @author ajs6f
048 * @since Feb 19, 2013
049 */
050public class FedoraEventImpl implements FedoraEvent {
051
052    private Event e;
053    private final String eventID;
054
055    private Set<EventType> eventTypes = new HashSet<>();
056    private Set<String> eventProperties = new HashSet<>();
057    private static final List<Integer> PROPERTY_TYPES = Arrays.asList(PROPERTY_ADDED, PROPERTY_CHANGED,
058            PROPERTY_REMOVED);
059
060    private static final UniqueValueSupplier pidMinter = new DefaultPathMinter();
061
062    /**
063     * Wrap a JCR Event with our FedoraEvent decorators
064     *
065     * @param e the JCR event
066     */
067    public FedoraEventImpl(final Event e) {
068        checkArgument(e != null, "null cannot support a FedoraEvent!");
069        eventID = pidMinter.get();
070        this.e = e;
071    }
072
073    /**
074     * Create a FedoraEvent from an existing FedoraEvent object
075     * Note: Only the wrapped JCR event is passed on to the new object.
076     *
077     * @param e the given fedora event
078     */
079    public FedoraEventImpl(final FedoraEvent e) {
080        checkArgument(e != null, "null cannot support a FedoraEvent!");
081        eventID = e.getEventID();
082        this.e = ((FedoraEventImpl)e).e;
083    }
084
085    /**
086     * @return the event types of the underlying JCR {@link Event}s
087     */
088    @Override
089    public Set<EventType> getTypes() {
090        final EventType type = valueOf(e.getType());
091        return eventTypes != null ? union(singleton(type), eventTypes) : singleton(type);
092    }
093
094    /**
095     * @param type the type
096     * @return this object for continued use
097     */
098    @Override
099    public FedoraEvent addType(final EventType type) {
100        eventTypes.add(type);
101        return this;
102    }
103
104    /**
105     * @return the property names of the underlying JCR property {@link Event}s
106    **/
107    @Override
108    public Set<String> getProperties() {
109        return eventProperties;
110    }
111
112    /**
113     * Add a property name to this event
114     * @param property property name
115     * @return this object for continued use
116    **/
117    @Override
118    public FedoraEvent addProperty( final String property ) {
119        eventProperties.add(property);
120        return this;
121    }
122
123    /**
124     * @return the path of the underlying JCR {@link Event}s
125     */
126    @Override
127    public String getPath() {
128        return getPath(e);
129    }
130
131    /**
132     * Get the path of the node related to this event (removing property names
133     * from the end of property nodes).
134     * @param e JCR Event
135     * @return the node path for this event
136    **/
137    public static String getPath(final Event e) {
138        try {
139            final String path;
140            if (PROPERTY_TYPES.contains(e.getType())) {
141                path = e.getPath().substring(0, e.getPath().lastIndexOf("/"));
142            } else {
143                path = e.getPath();
144            }
145            return path.replaceAll("/" + JCR_CONTENT, "");
146        } catch (RepositoryException e1) {
147            throw new RepositoryRuntimeException("Error getting event path!", e1);
148        }
149    }
150
151    /**
152     * @return the user ID of the underlying JCR {@link Event}s
153     */
154    @Override
155    public String getUserID() {
156        return e.getUserID();
157    }
158
159    /**
160     * @return the user data of the underlying JCR {@link Event}s
161     */
162    @Override
163    public String getUserData() {
164        try {
165            return e.getUserData();
166        } catch (RepositoryException e1) {
167            throw new RepositoryRuntimeException("Error getting event userData!", e1);
168        }
169    }
170
171    /**
172     * @return the date of the underlying JCR {@link Event}s
173     */
174    @Override
175    public long getDate() {
176        try {
177            return e.getDate();
178        } catch (RepositoryException e1) {
179            throw new RepositoryRuntimeException("Error getting event date!", e1);
180        }
181    }
182
183    /**
184     * Get the event ID.
185     * @return Event identifier to use for building event URIs (e.g., in an external triplestore).
186    **/
187    @Override
188    public String getEventID() {
189        return eventID;
190    }
191
192    @Override
193    public String toString() {
194
195        return toStringHelper(this)
196            .add("Event types:", String.join(",", getTypes().stream()
197                            .map(EventType::getName)
198                            .collect(toList())))
199            .add("Event properties:", String.join(",", eventProperties))
200            .add("Path:", getPath())
201            .add("Date: ", getDate()).toString();
202    }
203
204    private static class DefaultPathMinter implements HierarchicalIdentifierSupplier { }
205}