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.kernel.impl.observer; 020 021import com.google.common.collect.Multimap; 022import com.google.common.collect.MultimapBuilder; 023import com.google.common.eventbus.EventBus; 024import org.fcrepo.kernel.api.identifiers.FedoraId; 025import org.fcrepo.kernel.api.models.ResourceFactory; 026import org.fcrepo.kernel.api.observer.EventAccumulator; 027import org.fcrepo.kernel.api.operations.ResourceOperation; 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030import org.springframework.stereotype.Component; 031 032import javax.inject.Inject; 033import java.net.URI; 034import java.util.Collections; 035import java.util.Map; 036import java.util.Set; 037import java.util.concurrent.ConcurrentHashMap; 038import java.util.stream.Collectors; 039 040import static com.google.common.base.Preconditions.checkNotNull; 041import static com.google.common.base.Strings.emptyToNull; 042 043/** 044 * @author pwinckles 045 */ 046@Component 047public class EventAccumulatorImpl implements EventAccumulator { 048 049 private final static Logger LOG = LoggerFactory.getLogger(EventAccumulatorImpl.class); 050 051 private final Map<String, Multimap<FedoraId, EventBuilder>> transactionEventMap; 052 053 @Inject 054 private ResourceFactory resourceFactory; 055 056 @Inject 057 private EventBus eventBus; 058 059 public EventAccumulatorImpl() { 060 this.transactionEventMap = new ConcurrentHashMap<>(); 061 } 062 063 @Override 064 public void recordEventForOperation(final String transactionId, final FedoraId fedoraId, 065 final ResourceOperation operation) { 066 checkNotNull(emptyToNull(transactionId), "transactionId cannot be blank"); 067 checkNotNull(fedoraId, "fedoraId cannot be null"); 068 069 final var events = transactionEventMap.computeIfAbsent(transactionId, key -> 070 MultimapBuilder.hashKeys().arrayListValues().build()); 071 final var eventBuilder = ResourceOperationEventBuilder.fromResourceOperation(fedoraId, operation); 072 events.put(fedoraId, eventBuilder); 073 } 074 075 @Override 076 public void emitEvents(final String transactionId, final String baseUrl, final String userAgent) { 077 LOG.debug("Emitting events for transaction {}", transactionId); 078 079 final var eventMap = transactionEventMap.remove(transactionId); 080 081 if (eventMap != null) { 082 eventMap.keySet().forEach(fedoraId -> { 083 final var events = eventMap.get(fedoraId); 084 085 try { 086 final var mergedBuilder = events.stream() 087 .reduce(EventBuilder::merge).get(); 088 089 final var event = mergedBuilder 090 .withResourceTypes(loadResourceTypes(fedoraId)) 091 .withBaseUrl(baseUrl) 092 .withUserAgent(userAgent) 093 .build(); 094 095 LOG.debug("Emitting event: {}", event); 096 eventBus.post(event); 097 } catch (Exception e) { 098 LOG.error("Failed to emit events: {}", events, e); 099 } 100 }); 101 } 102 } 103 104 @Override 105 public void clearEvents(final String transactionId) { 106 LOG.trace("Clearing events for transaction {}", transactionId); 107 transactionEventMap.remove(transactionId); 108 } 109 110 private Set<String> loadResourceTypes(final FedoraId fedoraId) { 111 try { 112 return resourceFactory.getResource(fedoraId).getTypes().stream() 113 .map(URI::toString) 114 .collect(Collectors.toSet()); 115 } catch (Exception e) { 116 LOG.debug("Could not load resource types for {}", fedoraId, e); 117 // This can happen if the resource no longer exists 118 return Collections.emptySet(); 119 } 120 } 121 122}