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