001/* 002 * Licensed to DuraSpace under one or more contributor license agreements. 003 * See the NOTICE file distributed with this work for additional information 004 * regarding copyright ownership. 005 * 006 * DuraSpace licenses this file to you under the Apache License, 007 * Version 2.0 (the "License"); you may not use this file except in 008 * compliance with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.fcrepo.event.serialization; 020 021import static java.time.format.DateTimeFormatter.ISO_INSTANT; 022import static java.util.stream.Collectors.toList; 023 024import static org.fcrepo.kernel.api.RdfLexicon.ACTIVITY_STREAMS_NAMESPACE; 025import static org.fcrepo.kernel.api.RdfLexicon.PROV_NAMESPACE; 026import static org.slf4j.LoggerFactory.getLogger; 027 028import java.time.Instant; 029import java.util.ArrayList; 030import java.util.Arrays; 031import java.util.List; 032import java.util.Objects; 033 034import com.fasterxml.jackson.annotation.JsonSubTypes; 035import com.fasterxml.jackson.annotation.JsonTypeInfo; 036import org.fcrepo.kernel.api.observer.Event; 037 038import org.slf4j.Logger; 039 040import com.fasterxml.jackson.annotation.JsonIgnore; 041import com.fasterxml.jackson.annotation.JsonProperty; 042 043/** 044 * A structure used for serializing a Event into JSON 045 * 046 * @author acoburn 047 * @author dbernstein 048 * @author whikloj 049 */ 050public class JsonLDEventMessage { 051 052 @JsonIgnore 053 private static final Logger LOGGER = getLogger(JsonLDEventMessage.class); 054 055 public static class ContextElement { 056 057 @JsonProperty("@id") 058 public final String id; 059 060 @JsonProperty("@type") 061 public final String type; 062 063 public ContextElement(final String id) { 064 this.id = id; 065 this.type = "@id"; 066 } 067 068 public ContextElement(final String id, final String type) { 069 this.id = id; 070 this.type = type; 071 } 072 } 073 074 public static class Context { 075 076 public final String prov = "http://www.w3.org/ns/prov#"; 077 078 public final String dcterms = "http://purl.org/dc/terms/"; 079 080 public final String type = "@type"; 081 082 public final String id = "@id"; 083 084 public final ContextElement isPartOf = new ContextElement("dcterms:isPartOf"); 085 086 } 087 088 public static class Object { 089 090 @JsonProperty("type") 091 public List<String> type; 092 093 @JsonProperty("id") 094 public String id; 095 096 @JsonProperty("isPartOf") 097 public String isPartOf; 098 099 public Object() { 100 // Needed to deserialize class. 101 } 102 103 public Object(final String id, final List<String> type, final String isPartOf) { 104 this.type = type; 105 this.id = id; 106 this.isPartOf = isPartOf; 107 } 108 } 109 110 @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 111 @JsonSubTypes({ 112 @JsonSubTypes.Type(value = Application.class, name = "Application"), 113 @JsonSubTypes.Type(value = Person.class, name = "Person") } 114 ) 115 public static class Actor { 116 @JsonIgnore 117 public String type; 118 119 public Actor(final String type) { 120 this.type = type; 121 } 122 } 123 124 public static class Application extends Actor { 125 126 @JsonProperty("name") 127 public String name; 128 129 public Application() { 130 super("Application"); 131 } 132 133 public Application(final String name, final String type) { 134 super(type); 135 this.name = name; 136 } 137 } 138 139 public static class Person extends Actor { 140 141 @JsonProperty("id") 142 public String id; 143 144 public Person() { 145 super("Person"); 146 } 147 148 public Person(final String id, final String type) { 149 super(type); 150 this.id = id; 151 } 152 } 153 154 @JsonProperty("id") 155 public String id; 156 157 @JsonProperty("type") 158 public List<String> type; 159 160 @JsonProperty("name") 161 public String name; 162 163 @JsonProperty("published") 164 public Instant published; 165 166 167 @JsonProperty("actor") 168 public List<Actor> actor; 169 170 @JsonProperty("object") 171 public Object object; 172 173 @JsonProperty("@context") 174 public List<java.lang.Object> context; 175 176 public void setPublished(final String published) { 177 this.published = Instant.from(ISO_INSTANT.parse(published)); 178 } 179 180 /** 181 * Populate a JsonLDEventMessage from a Event 182 * 183 * @param evt The Fedora event 184 * @return a JsonLDEventMessage 185 */ 186 public static JsonLDEventMessage from(final Event evt) { 187 188 final String baseUrl = evt.getBaseUrl(); 189 190 // build objectId 191 final String objectId = baseUrl + evt.getPath(); 192 // build event types list 193 final List<String> types = evt.getTypes() 194 .stream() 195 .map(rdfType -> rdfType.getTypeAbbreviated()) 196 .collect(toList()); 197 // comma-separated list for names of events (since name requires string rather than array) 198 final String name = String.join(", ", evt.getTypes() 199 .stream() 200 .map(rdfType -> rdfType.getName()) 201 .collect(toList())); 202 // build resource types list 203 final List<String> resourceTypes = new ArrayList<>(evt.getResourceTypes()); 204 if (!resourceTypes.contains(PROV_NAMESPACE + "Entity")) { 205 resourceTypes.add(PROV_NAMESPACE + "Entity"); 206 } 207 208 // build actors list 209 final List<Actor> actor = new ArrayList<>(); 210 actor.add(new Person(Objects.toString(evt.getUserURI()), "Person")); 211 final String softwareAgent = evt.getUserAgent(); 212 if (softwareAgent != null) { 213 actor.add(new Application(softwareAgent, "Application")); 214 } 215 216 final JsonLDEventMessage msg = new JsonLDEventMessage(); 217 218 msg.id = evt.getEventID(); 219 msg.context = Arrays.asList(ACTIVITY_STREAMS_NAMESPACE, new Context()); 220 msg.actor = actor; 221 msg.published = evt.getDate(); 222 msg.type = types; 223 msg.name = name; 224 msg.object = new Object(objectId, resourceTypes, baseUrl); 225 return msg; 226 } 227}