Skip to content

Commit

Permalink
Merge pull request #153 from mbari-media-management/feature/swingtable
Browse files Browse the repository at this point in the history
Feature/swingtable
  • Loading branch information
hohonuuli committed Feb 16, 2023
2 parents 4e2456e + 82ae211 commit c90aeae
Show file tree
Hide file tree
Showing 48 changed files with 1,639 additions and 83 deletions.
20 changes: 11 additions & 9 deletions build.gradle
Expand Up @@ -9,7 +9,7 @@ plugins {

subprojects {

version = "1.4.6"
version = "1.5.0"

apply plugin: 'com.adarshr.test-logger'
apply plugin: 'com.github.ben-manes.versions'
Expand Down Expand Up @@ -54,13 +54,14 @@ subprojects {
javafxVersion = "19.0.2"
}


dependencies {

constraints {
implementation 'com.auth0:java-jwt:3.18.3'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.1'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.2'
implementation 'com.fatboyindustrial.gson-javatime-serialisers:gson-javatime-serialisers:1.1.2'
implementation 'com.github.ben-manes.caffeine:caffeine:3.1.2'
implementation 'com.github.ben-manes.caffeine:caffeine:3.1.3'
implementation 'com.github.mizosoft.methanol:methanol:1.7.0'
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.google.inject:guice:5.1.0'
Expand All @@ -80,19 +81,20 @@ subprojects {
implementation 'org.glassfish.jersey.core:jersey-client:2.29'
implementation 'org.kordamp.ikonli:ikonli-javafx:12.3.1'
implementation 'org.kordamp.ikonli:ikonli-material-pack:12.3.1'
implementation 'org.zeromq:jeromq:0.5.3'
implementation 'org.kordamp.ikonli:ikonli-swing:12.3.1'
implementation 'org.zeromq:jeromq:0.5.2'
implementation 'org.mbari:imgfx:0.0.13'
implementation 'org.mbari.vcr4j:vcr4j-core:5.2.0-SNAPSHOT'
implementation 'org.mbari.vcr4j:vcr4j-remote:5.2.0-SNAPSHOT'
implementation 'org.mbari.vcr4j:vcr4j-sharktopoda-client:5.2.0-SNAPSHOT'
implementation 'org.mbari.vcr4j:vcr4j-sharktopoda:5.2.0-SNAPSHOT'
implementation 'org.mbari.vcr4j:vcr4j-core:5.2.0'
implementation 'org.mbari.vcr4j:vcr4j-remote:5.2.0'
implementation 'org.mbari.vcr4j:vcr4j-sharktopoda-client:5.2.0'
implementation 'org.mbari.vcr4j:vcr4j-sharktopoda:5.2.0'
implementation 'org.mbari:mbarix4j:2.0.5.jre11'
implementation 'org.slf4j:slf4j-api:2.0.6'
implementation 'org.slf4j:slf4j-jdk14:2.0.6'
implementation 'org.slf4j:slf4j-jdk-platform-logging:2.0.6'
implementation 'org.swinglabs.swingx:swingx-all:1.6.5-1'
runtimeOnly 'ch.qos.logback:logback-classic:1.4.5'
runtimeOnly 'org.fusesource.jansi:jansi:2.4.0'
runtimeOnly 'org.slf4j:slf4j-jdk14:2.0.3'
}

testCompileOnly 'junit:junit:4.13.2'
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
3 changes: 2 additions & 1 deletion gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
12 changes: 8 additions & 4 deletions gradlew
Expand Up @@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
Expand All @@ -80,10 +80,10 @@ do
esac
done

APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit

APP_NAME="Gradle"
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
Expand Down Expand Up @@ -143,12 +143,16 @@ fi
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
Expand Down
1 change: 1 addition & 0 deletions gradlew.bat
Expand Up @@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

Expand Down
Expand Up @@ -21,6 +21,8 @@ public class CachedConceptService implements ConceptService {

private final ConceptService conceptService;
private volatile String rootName;

// This list is unmodifiable. It gets set to a populated list as needed in `findAllNames`
private volatile List<String> allNames = Collections.emptyList();

private final AsyncLoadingCache<String, Optional<Concept>> conceptCache;
Expand All @@ -44,7 +46,7 @@ public CachedConceptService(ConceptService conceptService) {

public synchronized void clear() {
rootName = null;
allNames.clear();
allNames = Collections.emptyList();
conceptCache.synchronous().invalidateAll();
templateCache.synchronous().invalidateAll();
}
Expand Down
@@ -0,0 +1,185 @@
package org.mbari.vars.services;

import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.mbari.vars.services.model.LastUpdate;
import org.mbari.vars.services.model.Media;

import java.net.URI;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class CachedMediaService implements MediaService {

private final MediaService mediaService;
private final AsyncLoadingCache<UUID, Optional<Media>> mediaCache;

public CachedMediaService(MediaService mediaService) {
this.mediaService = mediaService;
mediaCache = Caffeine.newBuilder()
.expireAfterWrite(15, TimeUnit.MINUTES)
.maximumSize(100)
.buildAsync((key, executor) -> mediaService.findByUuid(key).thenApply(Optional::ofNullable));
}

public synchronized void clear() {
mediaCache.synchronous().invalidateAll();
}

@Override
public CompletableFuture<Media> create(Media media) {
return cacheSingle(mediaService.create(media));
}

@Override
public CompletableFuture<Media> create(String videoSequenceName, String cameraId, String videoName, URI uri, Instant startTimestamp) {
return cacheSingle(mediaService.create(videoSequenceName, cameraId, videoName, uri, startTimestamp));
}

@Override
public CompletableFuture<Media> update(UUID videoReferenceUuid, Instant startTimestamp, Duration duration) {
return cacheSingle(mediaService.update(videoReferenceUuid, startTimestamp, duration));
}

@Override
public CompletableFuture<Media> update(Media media) {
return cacheSingle(mediaService.update(media));
}

@Override
public CompletableFuture<Boolean> delete(UUID videoReferenceUuid) {
return mediaService.delete(videoReferenceUuid)
.thenApply(ok -> {
if (ok) {
mediaCache.synchronous().invalidate(videoReferenceUuid);
}
return ok;
});
}

@Override
public CompletableFuture<Media> findByUuid(UUID uuid) {
return mediaCache.get(uuid).thenApply(m -> m.orElse(null));
}


@Override
public CompletableFuture<Media> findBySha512(byte[] sha512) {
return cacheSingle(mediaService.findBySha512(sha512));
}

@Override
public CompletableFuture<Media> findByUri(URI uri) {
return cacheSingle(mediaService.findByUri(uri));
}

@Override
public CompletableFuture<List<Media>> findByVideoSequenceName(String videoSequenceName) {
return cacheMany(mediaService.findByVideoSequenceName(videoSequenceName));
}

@Override
public CompletableFuture<List<Media>> findByVideoName(String videoName) {
return cacheMany(mediaService.findByVideoName(videoName));
}

@Override
public CompletableFuture<List<String>> findAllVideoSequenceNames() {
return mediaService.findAllVideoSequenceNames();
}

@Override
public CompletableFuture<List<Media>> findByCameraIdAndTimestamp(String cameraId, Instant timestamp) {
return cacheMany(mediaService.findByCameraIdAndTimestamp(cameraId, timestamp));

}

@Override
public CompletableFuture<List<Media>> findByCameraIdAndDate(String cameraId, Instant startTimestamp, Instant endTimestamp) {
return cacheMany(mediaService.findByCameraIdAndDate(cameraId, startTimestamp, endTimestamp));
}

@Override
public CompletableFuture<List<Media>> findByVideoSequenceNameAndTimestamp(String videoSequenceName, Instant timestamp) {
return cacheMany(mediaService.findByVideoSequenceNameAndTimestamp(videoSequenceName, timestamp));
}

@Override
public CompletableFuture<List<String>> findAllCameraIds() {
return mediaService.findAllCameraIds();
}

@Override
public CompletableFuture<List<URI>> findAllURIs() {
return mediaService.findAllURIs();
}

@Override
public CompletableFuture<List<Media>> findConcurrentByVideoReferenceUuid(UUID uuid) {
return cacheMany(mediaService.findConcurrentByVideoReferenceUuid(uuid));
}

@Override
public CompletableFuture<List<Media>> findByFilename(String filename) {
return cacheMany(mediaService.findByFilename(filename));

}

@Override
public CompletableFuture<LastUpdate> findLastVideoSequenceUpdate(UUID uuid) {
return mediaService.findLastVideoSequenceUpdate(uuid);
}

@Override
public CompletableFuture<LastUpdate> findLastVideoUpdate(UUID uuid) {
return mediaService.findLastVideoUpdate(uuid);
}

@Override
public CompletableFuture<LastUpdate> findLastVideoReferenceUpdate(UUID uuid) {
return mediaService.findLastVideoReferenceUpdate(uuid);
}

@Override
public CompletableFuture<List<String>> findVideoSequenceNamesByCameraId(String cameraId) {
return mediaService.findVideoSequenceNamesByCameraId(cameraId);
}

@Override
public CompletableFuture<List<String>> findVideoNamesByVideoSequenceName(String videoSequenceName) {
return mediaService.findVideoNamesByVideoSequenceName(videoSequenceName);
}

private CompletableFuture<List<Media>> cacheMany(CompletableFuture<List<Media>> future) {
return future.thenApply(xs -> {
if (xs != null) {
var cache = mediaCache.synchronous();
xs.forEach(m -> cache.put(m.getVideoReferenceUuid(), Optional.of(m)));
}
return xs;
});
}

private CompletableFuture<Media> cacheSingle(CompletableFuture<Media> future) {
return future.thenApply(m -> {
if (m != null) {
mediaCache.synchronous().put(m.getVideoReferenceUuid(), Optional.of(m));
}
return m;
});
}

// private CompletableFuture<Media> invalidateSingle(CompletableFuture<Media> future) {
// return future.thenApply(m -> {
// if (m != null) {
// mediaCache.synchronous().invalidate(m.getVideoReferenceUuid());
// }
// return m;
// });
// }
}
Expand Up @@ -93,11 +93,12 @@ public static Services buildForUI(List<EndpointConfig> endpoints) {
var mediaE = namedEndpoints.get("vampire-squid");
var userE = namedEndpoints.get("vars-user-server");
var prefs = buildPrefs(userE.getUrl().toExternalForm(), userE.getTimeout(), userE.getSecret());
var mediaService = buildMediaService(mediaE.getUrl().toExternalForm(), mediaE.getTimeout(), mediaE.getSecret());
return new Services(
buildAnnotationService(annoE.getUrl().toExternalForm(), annoE.getTimeout(), annoE.getSecret()),
buildConceptService(kbE.getUrl().toExternalForm(), kbE.getTimeout(), kbE.getSecret()),
buildImageArchiveService(imgE.getUrl().toExternalForm(), imgE.getTimeout(), imgE.getSecret()),
buildMediaService(mediaE.getUrl().toExternalForm(), mediaE.getTimeout(), mediaE.getSecret()),
new CachedMediaService(mediaService),
buildUserService(userE.getUrl().toExternalForm(), userE.getTimeout(), userE.getSecret()),
prefs.getPreferencesService(),
prefs.getPreferencesFactory()
Expand Down
Expand Up @@ -159,4 +159,6 @@ public static Optional<Association> parse(String s) {
return a.getLinkValue().compareTo(b.getLinkValue());
}
};

public static final Comparator<Association> ALPHABETICAL_COMPARATOR = Comparator.comparing(Association::toString);
}
Expand Up @@ -4,6 +4,7 @@
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Optional;
import java.util.UUID;

/**
Expand Down Expand Up @@ -237,4 +238,14 @@ public boolean equals(Object o) {
public int hashCode() {
return videoReferenceUuid.hashCode();
}

public Optional<Duration> toMediaElapsedTime(Annotation annotation) {
if (annotation.getVideoReferenceUuid().equals(videoReferenceUuid) && annotation.getElapsedTime() != null) {
return Optional.of(annotation.getElapsedTime());
}
else if (annotation.getRecordedTimestamp() != null && startTimestamp != null) {
return Optional.of(Duration.between(startTimestamp, annotation.getRecordedTimestamp()));
}
return Optional.empty();
}
}
@@ -0,0 +1,38 @@
package org.mbari.vars.services.model;

import org.junit.Test;
import static org.junit.Assert.*;
import org.mbari.vcr4j.VideoIndex;

import java.time.Duration;
import java.time.Instant;
import java.util.UUID;

public class MediaTest {

@Test
public void testToMediaElaspsedTime1() {
var timestamp = Instant.parse("2000-01-01T00:00:00Z");
var elapsedTime = Duration.ofMillis(1000);
var recordedTimestamp = timestamp.plus(elapsedTime);

var media = new Media();
media.setVideoReferenceUuid(UUID.randomUUID());
media.setStartTimestamp(timestamp);
var a0 = new Annotation("Media UUID matches", "brian", new VideoIndex(elapsedTime), media.getVideoReferenceUuid());
var a1 = new Annotation("Media UUID does not match", "brian", new VideoIndex(Duration.ZERO, recordedTimestamp), UUID.randomUUID());

// Same videoReferenceUUID should return annos elapsedTime as is
var opt1 = media.toMediaElapsedTime(a0);
assertTrue(opt1.isPresent());
assertEquals(elapsedTime, opt1.get());

// Different videoReferenceUuid should munge the annos elapsedTime relative to the media using
// the annos recordedTimestamp. Annos elapsedTime = 9, but it'sr ecorded date is 1 seconds from
// the start of the video
var opt2 = media.toMediaElapsedTime(a1);
assertTrue(opt2.isPresent());
assertEquals(elapsedTime, opt2.get());

}
}

0 comments on commit c90aeae

Please sign in to comment.