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.kernel.impl.observer; 008 009import static java.nio.charset.StandardCharsets.UTF_8; 010 011import java.net.URI; 012import java.net.URLEncoder; 013import java.time.Instant; 014import java.util.HashSet; 015import java.util.Objects; 016import java.util.Set; 017 018import org.fcrepo.kernel.api.identifiers.FedoraId; 019import org.fcrepo.kernel.api.observer.Event; 020import org.fcrepo.kernel.api.observer.EventType; 021import org.fcrepo.kernel.api.operations.ResourceOperation; 022import org.fcrepo.kernel.impl.util.UserUtil; 023 024/** 025 * Converts a ResourceOperation into an Event. 026 * 027 * @author pwinckles 028 */ 029public class ResourceOperationEventBuilder implements EventBuilder { 030 031 private FedoraId fedoraId; 032 private Set<EventType> types; 033 private Set<String> resourceTypes; 034 private String userID; 035 private String userAgent; 036 private String baseUrl; 037 private Instant date; 038 private String userAgentBaseUri; 039 040 /** 041 * Creates a new EventBuilder based on an ResourceOperation 042 * 043 * @param fedoraId the FedoraId the operation is on 044 * @param operation the ResourceOperation to create an event for 045 * @param userAgentBaseUri the base uri of the user agent, optional 046 * @return new builder 047 */ 048 public static ResourceOperationEventBuilder fromResourceOperation(final FedoraId fedoraId, 049 final ResourceOperation operation, 050 final String userAgentBaseUri) { 051 final var builder = new ResourceOperationEventBuilder(); 052 builder.fedoraId = fedoraId; 053 builder.date = Instant.now(); 054 builder.resourceTypes = new HashSet<>(); 055 builder.userID = operation.getUserPrincipal(); 056 builder.types = new HashSet<>(); 057 builder.types.add(mapOperationToEventType(operation)); 058 builder.userAgentBaseUri = userAgentBaseUri; 059 return builder; 060 } 061 062 private static EventType mapOperationToEventType(final ResourceOperation operation) { 063 switch (operation.getType()) { 064 case CREATE: 065 case OVERWRITE_TOMBSTONE: 066 return EventType.RESOURCE_CREATION; 067 case UPDATE: 068 case UPDATE_HEADERS: 069 return EventType.RESOURCE_MODIFICATION; 070 case DELETE: 071 return EventType.RESOURCE_DELETION; 072 case PURGE: 073 return EventType.RESOURCE_PURGE; 074 case FOLLOW: 075 return EventType.INBOUND_REFERENCE; 076 default: 077 throw new IllegalStateException( 078 String.format("There is no EventType mapping for ResourceOperation type %s on operation %s", 079 operation.getType(), operation)); 080 } 081 } 082 083 private ResourceOperationEventBuilder() { 084 // Intentionally left blank 085 } 086 087 @Override 088 public EventBuilder merge(final EventBuilder other) { 089 if (other == null) { 090 return this; 091 } 092 093 if (!(other instanceof ResourceOperationEventBuilder)) { 094 throw new IllegalStateException( 095 String.format("Cannot merge EventBuilders because they are different types <%s> and <%s>", 096 this.getClass(), other.getClass())); 097 } 098 099 final var otherCast = (ResourceOperationEventBuilder) other; 100 101 if (!this.fedoraId.equals(otherCast.fedoraId)) { 102 throw new IllegalStateException( 103 String.format("Cannot merge events because they are for different resources: <%s> and <%s>", 104 this, otherCast)); 105 } 106 107 this.types.addAll(otherCast.types); 108 this.resourceTypes.addAll(otherCast.resourceTypes); 109 110 if (this.date.isBefore(otherCast.date)) { 111 this.date = otherCast.date; 112 } 113 114 return this; 115 } 116 117 @Override 118 public EventBuilder withResourceTypes(final Set<String> resourceTypes) { 119 this.resourceTypes = Objects.requireNonNullElse(resourceTypes, new HashSet<>()); 120 return this; 121 } 122 123 @Override 124 public EventBuilder withBaseUrl(final String baseUrl) { 125 this.baseUrl = baseUrl; 126 return this; 127 } 128 129 @Override 130 public EventBuilder withUserAgent(final String userAgent) { 131 this.userAgent = (userAgent.contains(" ") ? URLEncoder.encode(userAgent, UTF_8) : userAgent); 132 return this; 133 } 134 135 @Override 136 public Event build() { 137 URI userUri = null; 138 if (userID != null) { 139 userUri = UserUtil.getUserURI(userID, userAgentBaseUri); 140 } 141 return new EventImpl(fedoraId, types, resourceTypes, userID, userUri, userAgent, baseUrl, date); 142 } 143 144 @Override 145 public String toString() { 146 return "ResourceOperationEventBuilder{" + 147 "fedoraId=" + fedoraId + 148 ", types=" + types + 149 ", resourceTypes=" + resourceTypes + 150 ", userID='" + userID + '\'' + 151 ", userAgent='" + userAgent + '\'' + 152 ", baseUrl='" + baseUrl + '\'' + 153 ", date=" + date + 154 '}'; 155 } 156 157}