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