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