Skip to content

Commit

Permalink
[Performance #287] Introduce base class for Vocabulary related event
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaskabc authored and ledsoft committed Sep 13, 2024
1 parent 36e0a7f commit 3a86e8b
Show file tree
Hide file tree
Showing 12 changed files with 77 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,17 @@
package cz.cvut.kbss.termit.event;

import org.jetbrains.annotations.NotNull;
import org.springframework.context.ApplicationEvent;

import java.net.URI;
import java.util.Objects;

/**
* Represents an event of modification of the content of a vocabulary.
* <p>
* This typically means a term is added, removed or modified. Modification of vocabulary metadata themselves is not considered here.
*/
public class VocabularyContentModifiedEvent extends ApplicationEvent {

private final URI vocabularyIri;
public class VocabularyContentModifiedEvent extends VocabularyEvent {

public VocabularyContentModifiedEvent(Object source, @NotNull URI vocabularyIri) {
super(source);
Objects.requireNonNull(vocabularyIri);
this.vocabularyIri = vocabularyIri;
}

public URI getVocabularyIri() {
return vocabularyIri;
super(source, vocabularyIri);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@
*/
package cz.cvut.kbss.termit.event;

import org.springframework.context.ApplicationEvent;
import org.jetbrains.annotations.NotNull;

import java.net.URI;

/**
* Indicates that a vocabulary has been created.
*/
public class VocabularyCreatedEvent extends ApplicationEvent {
public class VocabularyCreatedEvent extends VocabularyEvent {

public VocabularyCreatedEvent(Object source) {
super(source);
public VocabularyCreatedEvent(Object source, @NotNull URI vocabularyIri) {
super(source, vocabularyIri);
}
}
28 changes: 28 additions & 0 deletions src/main/java/cz/cvut/kbss/termit/event/VocabularyEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package cz.cvut.kbss.termit.event;

import org.jetbrains.annotations.NotNull;
import org.springframework.context.ApplicationEvent;

import java.net.URI;
import java.util.Objects;

/**
* Base class for vocabulary related events
*/
public abstract class VocabularyEvent extends ApplicationEvent {
protected final URI vocabularyIri;

protected VocabularyEvent(Object source, @NotNull URI vocabularyIri) {
super(source);
Objects.requireNonNull(vocabularyIri);
this.vocabularyIri = vocabularyIri;
}

/**
* The identifier of the vocabulary to which this event is bound
* @return vocabulary IRI
*/
public URI getVocabularyIri() {
return vocabularyIri;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
/**
* Indicates that validation for a set of vocabularies was finished.
*/
public class VocabularyValidationFinishedEvent extends ApplicationEvent {
public class VocabularyValidationFinishedEvent extends VocabularyEvent {

/**
* Vocabulary closure of {@link #originVocabularyIri}.
* IRIs of vocabularies that are imported by {@link #originVocabularyIri} and were part of the validation.
* Vocabulary closure of {@link #vocabularyIri}.
* IRIs of vocabularies that are imported by {@link #vocabularyIri} and were part of the validation.
*/
@NotNull
@Unmodifiable
Expand All @@ -27,25 +27,18 @@ public class VocabularyValidationFinishedEvent extends ApplicationEvent {
@Unmodifiable
private final Collection<ValidationResult> validationResults;

/**
* IRI of the vocabulary on which the validation was triggered.
*/
@NotNull
private final URI originVocabularyIri;

/**
* @param source the source of the event
* @param originVocabularyIri Vocabulary closure of {@link #originVocabularyIri}.
* @param originVocabularyIri Vocabulary closure of {@link #vocabularyIri}.
* @param vocabularyIris IRI of the vocabulary on which the validation was triggered.
* @param validationResults results of the validation
*/
public VocabularyValidationFinishedEvent(@NotNull Object source, @NotNull URI originVocabularyIri,
@NotNull Collection<URI> vocabularyIris,
@NotNull List<ValidationResult> validationResults) {
super(source);
super(source, originVocabularyIri);
this.vocabularyIris = Collections.unmodifiableCollection(vocabularyIris);
this.validationResults = Collections.unmodifiableCollection(validationResults);
this.originVocabularyIri = originVocabularyIri;
}

public @NotNull Collection<URI> getVocabularyIris() {
Expand All @@ -55,8 +48,4 @@ public VocabularyValidationFinishedEvent(@NotNull Object source, @NotNull URI or
public @NotNull Collection<ValidationResult> getValidationResults() {
return validationResults;
}

public @NotNull URI getOriginVocabularyIri() {
return originVocabularyIri;
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
package cz.cvut.kbss.termit.event;

import org.springframework.context.ApplicationEvent;
import org.jetbrains.annotations.NotNull;

import java.net.URI;

/**
* Indicates that a Vocabulary will be removed
*/
public class VocabularyWillBeRemovedEvent extends ApplicationEvent {
private final URI vocabulary;
public class VocabularyWillBeRemovedEvent extends VocabularyEvent {

public VocabularyWillBeRemovedEvent(Object source, URI vocabulary) {
super(source);
this.vocabulary = vocabulary;
}

public URI getVocabulary() {
return vocabulary;
public VocabularyWillBeRemovedEvent(Object source, @NotNull URI vocabularyIri) {
super(source, vocabularyIri);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import cz.cvut.kbss.termit.event.AssetPersistEvent;
import cz.cvut.kbss.termit.event.AssetUpdateEvent;
import cz.cvut.kbss.termit.event.RefreshLastModifiedEvent;
import cz.cvut.kbss.termit.event.VocabularyContentModifiedEvent;
import cz.cvut.kbss.termit.event.VocabularyWillBeRemovedEvent;
import cz.cvut.kbss.termit.exception.PersistenceException;
import cz.cvut.kbss.termit.model.Glossary;
Expand Down Expand Up @@ -196,6 +197,7 @@ public void persist(Vocabulary entity) {
}
refreshLastModified();
eventPublisher.publishEvent(new AssetPersistEvent(this, entity));
eventPublisher.publishEvent(new VocabularyContentModifiedEvent(this, entity.getUri()));
} catch (RuntimeException e) {
throw new PersistenceException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public ResourceService(ResourceRepositoryService repositoryService, DocumentMana
*/
@EventListener
public void onVocabularyRemoval(VocabularyWillBeRemovedEvent event) {
vocabularyService.find(event.getVocabulary()).ifPresent(vocabulary -> {
vocabularyService.find(event.getVocabularyIri()).ifPresent(vocabulary -> {
if(vocabulary.getDocument() != null) {
remove(vocabulary.getDocument());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import cz.cvut.kbss.termit.dto.listing.VocabularyDto;
import cz.cvut.kbss.termit.event.VocabularyContentModifiedEvent;
import cz.cvut.kbss.termit.event.VocabularyCreatedEvent;
import cz.cvut.kbss.termit.event.VocabularyEvent;
import cz.cvut.kbss.termit.exception.NotFoundException;
import cz.cvut.kbss.termit.model.Vocabulary;
import cz.cvut.kbss.termit.model.acl.AccessControlList;
Expand All @@ -51,6 +52,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.Lazy;
Expand Down Expand Up @@ -126,8 +128,8 @@ public VocabularyService(VocabularyRepositoryService repositoryService,
* The goal for this is to get the results cached and do not force users to wait for validation
* when they request it.
*/
@EventListener
public void onVocabularyContentModified(VocabularyContentModifiedEvent event) {
@EventListener({VocabularyContentModifiedEvent.class, VocabularyCreatedEvent.class})
public void onVocabularyContentModified(VocabularyEvent event) {
repositoryService.validateContents(event.getVocabularyIri());
}

Expand Down Expand Up @@ -181,7 +183,7 @@ public void persist(Vocabulary instance) {
repositoryService.persist(instance);
final AccessControlList acl = aclService.createFor(instance);
instance.setAcl(acl.getUri());
eventPublisher.publishEvent(new VocabularyCreatedEvent(instance));
eventPublisher.publishEvent(new VocabularyCreatedEvent(this, instance.getUri()));
}

@Override
Expand Down Expand Up @@ -244,7 +246,7 @@ public Vocabulary importVocabulary(boolean rename, MultipartFile file) {
final Vocabulary imported = repositoryService.importVocabulary(rename, file);
final AccessControlList acl = aclService.createFor(imported);
imported.setAcl(acl.getUri());
eventPublisher.publishEvent(new VocabularyCreatedEvent(imported));
eventPublisher.publishEvent(new VocabularyCreatedEvent(this, imported.getUri()));
return imported;
}

Expand Down Expand Up @@ -384,7 +386,7 @@ public Integer getTermCount(Vocabulary vocabulary) {
@PreAuthorize("@vocabularyAuthorizationService.canCreateSnapshot(#vocabulary)")
public Snapshot createSnapshot(Vocabulary vocabulary) {
final Snapshot s = getSnapshotCreator().createSnapshot(vocabulary);
eventPublisher.publishEvent(new VocabularyCreatedEvent(s));
eventPublisher.publishEvent(new VocabularyCreatedEvent(this, s.getUri()));
cloneAccessControlList(s, vocabulary);
return s;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public void onVocabularyValidationFinished(VocabularyValidationFinishedEvent eve
messagingTemplate.convertAndSend(
DESTINATION_VOCABULARIES_VALIDATION,
event.getValidationResults(),
Map.of("vocabulary", event.getOriginVocabularyIri(), "cached", false)
Map.of("vocabulary", event.getVocabularyIri(), "cached", false)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ void persistPublishesAssetPersistEvent() {
transactional(() -> sut.persist(voc));

final ArgumentCaptor<ApplicationEvent> captor = ArgumentCaptor.forClass(ApplicationEvent.class);
verify(eventPublisher).publishEvent(captor.capture());
verify(eventPublisher, atLeastOnce()).publishEvent(captor.capture());
final Optional<AssetPersistEvent> evt = captor.getAllValues().stream()
.filter(AssetPersistEvent.class::isInstance)
.map(AssetPersistEvent.class::cast).findFirst();
Expand Down Expand Up @@ -767,7 +767,7 @@ void removePublishesEventAndDropsGraph() {
VocabularyWillBeRemovedEvent event = eventCaptor.getValue();
assertNotNull(event);

assertEquals(event.getVocabulary(), vocabulary.getUri());
assertEquals(event.getVocabularyIri(), vocabulary.getUri());

assertFalse(em.createNativeQuery("ASK WHERE{ GRAPH ?vocabulary { ?s ?p ?o }}", Boolean.class)
.setParameter("vocabulary", vocabulary.getUri())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import cz.cvut.kbss.termit.event.VocabularyContentModifiedEvent;
import cz.cvut.kbss.termit.model.Term;
import cz.cvut.kbss.termit.model.validation.ValidationResult;
import cz.cvut.kbss.termit.util.throttle.ThrottledFuture;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand All @@ -35,7 +36,9 @@

import static cz.cvut.kbss.termit.util.throttle.TestFutureRunner.runFuture;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertIterableEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.anyCollection;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
Expand All @@ -62,43 +65,42 @@ void setUp() {

vocabulary = Generator.generateUri();
Term term = Generator.generateTermWithId(vocabulary);
validationResult = new ValidationResult()
.setTermUri(term.getUri());
validationResult = new ValidationResult().setTermUri(term.getUri());
}

@Test
void invokesInternalValidatorWhenNoResultsAreCached() throws Exception {
final List<ValidationResult> results = Collections.singletonList(validationResult);
when(validator.runValidation(anyCollection())).thenReturn(results);
when(validator.validate(any(), anyCollection())).thenReturn(ThrottledFuture.done(results));
final Set<URI> vocabularies = Collections.singleton(vocabulary);
final Collection<ValidationResult> result = runFuture(sut.validate(vocabulary, vocabularies));
assertEquals(results, result);
verify(validator).runValidation(vocabularies);
verify(validator).validate(vocabulary, vocabularies);
}

@Test
void returnsCachedResultsWhenArgumentsMatch() throws Exception {
final List<ValidationResult> results = Collections.singletonList(validationResult);
when(validator.runValidation(anyCollection())).thenReturn(results);
when(validator.validate(any(), anyCollection())).thenReturn(ThrottledFuture.done(results));
final Set<URI> vocabularies = Collections.singleton(vocabulary);
final Collection<ValidationResult> resultOne = runFuture(sut.validate(vocabulary, vocabularies));
verify(validator).runValidation(vocabularies);
verify(validator).validate(vocabulary, vocabularies);
final Collection<ValidationResult> resultTwo = runFuture(sut.validate(vocabulary, vocabularies));
assertEquals(resultOne, resultTwo);
assertSame(results, resultOne);
verifyNoMoreInteractions(validator);
assertIterableEquals(resultOne, resultTwo);
assertSame(results, resultOne);
}

@Test
void evictCacheClearsCachedValidationResults() throws Exception {
final List<ValidationResult> results = Collections.singletonList(validationResult);
when(validator.runValidation(anyCollection())).thenReturn(results);
when(validator.validate(any(), anyCollection())).thenReturn(ThrottledFuture.done(results));
final Set<URI> vocabularies = Collections.singleton(vocabulary);
final Collection<ValidationResult> resultOne = runFuture(sut.validate(vocabulary, vocabularies));
verify(validator).runValidation(vocabularies);
verify(validator).validate(vocabulary, vocabularies);
sut.evictVocabularyCache(new VocabularyContentModifiedEvent(this, vocabulary));
final Collection<ValidationResult> resultTwo = runFuture(sut.validate(vocabulary, vocabularies));
verify(validator, times(2)).runValidation(vocabularies);
verify(validator, times(2)).validate(vocabulary, vocabularies);
assertEquals(resultOne, resultTwo);
assertSame(results, resultOne);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.inOrder;
Expand Down Expand Up @@ -275,7 +276,7 @@ void updateAccessControlLevelRetrievesACLForVocabularyAndUpdatesSpecifiedRecord(
@Test
void persistCreatesAccessControlListAndSetsItOnVocabularyInstance() {
final AccessControlList acl = Generator.generateAccessControlList(true);
final Vocabulary toPersist = Generator.generateVocabulary();
final Vocabulary toPersist = Generator.generateVocabularyWithId();
when(aclService.createFor(toPersist)).thenReturn(acl);

sut.persist(toPersist);
Expand Down Expand Up @@ -379,9 +380,10 @@ void importNewVocabularyPublishesVocabularyCreatedEvent() {

sut.importVocabulary(false, fileToImport);
final ArgumentCaptor<ApplicationEvent> captor = ArgumentCaptor.forClass(ApplicationEvent.class);
verify(eventPublisher).publishEvent(captor.capture());
assertInstanceOf(VocabularyCreatedEvent.class, captor.getValue());
assertEquals(persisted, captor.getValue().getSource());
verify(eventPublisher, atLeastOnce()).publishEvent(captor.capture());
Optional<VocabularyCreatedEvent> event = captor.getAllValues().stream().filter(e -> e instanceof VocabularyCreatedEvent).map(e->(VocabularyCreatedEvent)e).findAny();
assertTrue(event.isPresent());
assertEquals(persisted.getUri(), event.get().getVocabularyIri());
}

@Test
Expand Down

0 comments on commit 3a86e8b

Please sign in to comment.