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.HashMap;
027import java.util.HashSet;
028import java.util.Map;
029import java.util.Set;
030import java.util.function.Supplier;
031
032import javax.jcr.RepositoryException;
033import javax.jcr.observation.Event;
034
035import org.fcrepo.kernel.api.exception.RepositoryRuntimeException;
036import org.fcrepo.kernel.api.utils.EventType;
037import org.fcrepo.mint.UUIDPathMinter;
038
039import com.google.common.base.Function;
040import com.google.common.base.Joiner;
041import com.google.common.collect.Iterables;
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 FedoraEvent {
051
052    private Event e;
053    private final String eventID;
054
055    private Set<Integer> eventTypes = new HashSet<>();
056    private Set<String> eventProperties = new HashSet<>();
057
058    private static final Supplier<String> pidMinter = new UUIDPathMinter();
059
060    /**
061     * Wrap a JCR Event with our FedoraEvent decorators
062     *
063     * @param e the JCR event
064     */
065    public FedoraEvent(final Event e) {
066        checkArgument(e != null, "null cannot support a FedoraEvent!");
067        eventID = pidMinter.get();
068        this.e = e;
069    }
070
071    /**
072     * Create a FedoraEvent from an existing FedoraEvent object
073     * Note: Only the wrapped JCR event is passed on to the new object.
074     *
075     * @param e the given fedora event
076     */
077    public FedoraEvent(final FedoraEvent e) {
078        checkArgument(e != null, "null cannot support a FedoraEvent!");
079        eventID = e.getEventID();
080        this.e = e.e;
081    }
082
083    /**
084     * @return the event types of the underlying JCR {@link Event}s
085     */
086    public Set<Integer> getTypes() {
087        return eventTypes != null ? union(singleton(e.getType()), eventTypes) : singleton(e.getType());
088    }
089
090    /**
091     * @param type the type
092     * @return this object for continued use
093     */
094    public FedoraEvent addType(final Integer type) {
095        eventTypes.add(type);
096        return this;
097    }
098
099    /**
100     * @return the property names of the underlying JCR property {@link Event}s
101    **/
102    public Set<String> getProperties() {
103        return eventProperties;
104    }
105
106    /**
107     * Add a property name to this event
108     * @param property property name
109     * @return this object for continued use
110    **/
111    public FedoraEvent addProperty( final String property ) {
112        eventProperties.add(property);
113        return this;
114    }
115
116    /**
117     * @return the path of the underlying JCR {@link Event}s
118     */
119    public String getPath() {
120        return getPath(e);
121    }
122
123    /**
124     * Get the path of the node related to this event (removing property names
125     * from the end of property nodes).
126     * @param e JCR Event
127    **/
128    public static String getPath(final Event e) {
129        try {
130            // TODO: It would be better for this test to use a constant collection of:
131            // - PROPERTY_ADDED, PROPERTY_CHANGED, PROPERTY_REMOVED and Collection.contains().
132            if (e.getType() == PROPERTY_ADDED   ||
133                e.getType() == PROPERTY_CHANGED ||
134                e.getType() == PROPERTY_REMOVED) {
135                return e.getPath().substring(0, e.getPath().lastIndexOf("/"));
136            }
137            return e.getPath();
138        } catch (RepositoryException e1) {
139            throw new RepositoryRuntimeException("Error getting event path!", e1);
140        }
141    }
142
143    /**
144     * @return the user ID of the underlying JCR {@link Event}s
145     */
146    public String getUserID() {
147        return e.getUserID();
148    }
149
150    /**
151     * @return the info map of the underlying JCR {@link Event}s
152     */
153    public Map<Object, Object> getInfo() {
154        try {
155            return new HashMap<>(e.getInfo());
156        } catch (RepositoryException e1) {
157            throw new RepositoryRuntimeException("Error getting event info!", e1);
158        }
159    }
160
161    /**
162     * @return the user data of the underlying JCR {@link Event}s
163     */
164    public String getUserData() {
165        try {
166            return e.getUserData();
167        } catch (RepositoryException e1) {
168            throw new RepositoryRuntimeException("Error getting event userData!", e1);
169        }
170    }
171
172    /**
173     * @return the date of the underlying JCR {@link Event}s
174     */
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    public String getEventID() {
188        return eventID;
189    }
190
191    @Override
192    public String toString() {
193        return toStringHelper(this).add("Event types:",
194            Joiner.on(',').join(Iterables.transform(getTypes(), new Function<Integer, String>() {
195
196                @Override
197                public String apply(final Integer type) {
198                    return EventType.valueOf(type).getName();
199                }
200            }))).add("Event properties:",
201            Joiner.on(',').join(eventProperties)).add("Path:", getPath()).add("Date: ",
202            getDate()).add("Info:", getInfo()).toString();
203    }
204}