Skip to content

Commit

Permalink
Thread-Safe implementation of the *InmemoryBackends (eclipse-basyx#400)
Browse files Browse the repository at this point in the history
* Refactor *InMemoryBackends to be thread-safe

* Refactor AASXFileServerInMemory Backend to be thread-safe
  • Loading branch information
mateusmolina-iese authored Aug 26, 2024
1 parent c44663d commit 68f1b42
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,99 +27,63 @@

import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.StreamSupport;

import org.eclipse.digitaltwin.aas4j.v3.model.SpecificAssetId;
import org.eclipse.digitaltwin.basyx.aasdiscoveryservice.backend.AasDiscoveryDocument;
import org.eclipse.digitaltwin.basyx.aasdiscoveryservice.core.model.AssetLink;
import org.springframework.data.repository.CrudRepository;
import org.springframework.lang.NonNull;

/**
* In-memory implementation of the {@link CrudRepository} for the AAS Discovery
*
* @author zielstor, fried
* @author zielstor, fried, mateusmolina
*/
public class AasDiscoveryInMemoryCrudRepository implements CrudRepository<AasDiscoveryDocument, String> {

private final Map<String, Set<AssetLink>> assetLinks = new LinkedHashMap<>();
private final Map<String, List<SpecificAssetId>> assetIds = new LinkedHashMap<>();
private final ConcurrentMap<String, Set<AssetLink>> assetLinks = new ConcurrentHashMap<>();
private final ConcurrentMap<String, List<SpecificAssetId>> assetIds = new ConcurrentHashMap<>();

@Override
public <S extends AasDiscoveryDocument> S save(S entity) {
Set<AssetLink> assetLinks = entity.getAssetLinks();
List<SpecificAssetId> assetIds = entity.getSpecificAssetIds();
public synchronized @NonNull <S extends AasDiscoveryDocument> S save(@NonNull S entity) {
String shellId = entity.getShellIdentifier();

this.assetLinks.put(shellId, assetLinks);
this.assetIds.put(shellId, assetIds);
this.assetLinks.put(shellId, entity.getAssetLinks());
this.assetIds.put(shellId, entity.getSpecificAssetIds());

return entity;
}

@Override
public <S extends AasDiscoveryDocument> Iterable<S> saveAll(Iterable<S> entities) {
for (S entity : entities) {
this.save(entity);
}
public @NonNull <S extends AasDiscoveryDocument> Iterable<S> saveAll(@NonNull Iterable<S> entities) {
entities.forEach(this::save);
return entities;
}

@Override
public Optional<AasDiscoveryDocument> findById(String id) {
Set<AssetLink> assetLinks = this.assetLinks.get(id);
List<SpecificAssetId> assetIds = this.assetIds.get(id);
if (assetIds == null) {
assetIds = new ArrayList<>();
}

if (assetLinks == null) {
assetLinks = new HashSet<>();
}
return Optional.of(new AasDiscoveryDocument(id, assetLinks, assetIds));
public @NonNull Optional<AasDiscoveryDocument> findById(@NonNull String id) {
return Optional.ofNullable(buildAasDiscoveryDocument(id));
}

@Override
public boolean existsById(String id) {
public boolean existsById(@NonNull String id) {
return this.assetLinks.containsKey(id);
}

@Override
public Iterable<AasDiscoveryDocument> findAll() {
List<AasDiscoveryDocument> result = new ArrayList<>();
for (String shellId : this.assetLinks.keySet()) {
Set<AssetLink> assetLinks = this.assetLinks.get(shellId);
List<SpecificAssetId> assetIds = this.assetIds.get(shellId);
if (assetIds == null) {
assetIds = new ArrayList<>();
}

if (assetLinks == null) {
assetLinks = new HashSet<>();
}
result.add(new AasDiscoveryDocument(shellId, assetLinks, assetIds));
}
return result;
public @NonNull Iterable<AasDiscoveryDocument> findAll() {
return assetLinks.keySet().stream().map(this::buildAasDiscoveryDocument).toList();
}

@Override
public Iterable<AasDiscoveryDocument> findAllById(Iterable<String> ids) {
List<AasDiscoveryDocument> result = new ArrayList<>();
for (String id : ids) {
Set<AssetLink> assetLinks = this.assetLinks.get(id);
List<SpecificAssetId> assetIds = this.assetIds.get(id);
if (assetIds == null) {
assetIds = new ArrayList<>();
}

if (assetLinks == null) {
assetLinks = new HashSet<>();
}
result.add(new AasDiscoveryDocument(id, assetLinks, assetIds));
}
return result;
public @NonNull Iterable<AasDiscoveryDocument> findAllById(@NonNull Iterable<String> ids) {
return StreamSupport.stream(ids.spliterator(), false).map(this::buildAasDiscoveryDocument).toList();
}

@Override
Expand All @@ -128,34 +92,47 @@ public long count() {
}

@Override
public void deleteById(String id) {
public synchronized void deleteById(@NonNull String id) {
this.assetLinks.remove(id);
this.assetIds.remove(id);
}

@Override
public void delete(AasDiscoveryDocument entity) {
public void delete(@NonNull AasDiscoveryDocument entity) {
this.deleteById(entity.getShellIdentifier());
}

@Override
public void deleteAllById(Iterable<? extends String> ids) {
public void deleteAllById(@NonNull Iterable<? extends String> ids) {
for (String id : ids) {
this.deleteById(id);
}
}

@Override
public void deleteAll(Iterable<? extends AasDiscoveryDocument> entities) {
public void deleteAll(@NonNull Iterable<? extends AasDiscoveryDocument> entities) {
for (AasDiscoveryDocument entity : entities) {
this.deleteById(entity.getShellIdentifier());
}
}

@Override
public void deleteAll() {
public synchronized void deleteAll() {
this.assetLinks.clear();
this.assetIds.clear();
}

private synchronized AasDiscoveryDocument buildAasDiscoveryDocument(String shellId) {
Set<AssetLink> assetLinksSet = assetLinks.get(shellId);
List<SpecificAssetId> assetIdsList = assetIds.get(shellId);

if (assetIdsList == null)
assetIdsList = new ArrayList<>();

if (assetLinksSet == null)
assetLinksSet = new HashSet<>();

return new AasDiscoveryDocument(shellId, assetLinksSet, assetIdsList);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@

package org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
import org.springframework.data.repository.CrudRepository;
import org.springframework.lang.NonNull;

/**
* InMemory implementation for the AAS backend
Expand All @@ -43,40 +44,39 @@
*/
public class AasInMemoryBackend implements CrudRepository<AssetAdministrationShell, String> {

private Map<String, AssetAdministrationShell> inMemoryStore = new LinkedHashMap<>();
private final ConcurrentMap<String, AssetAdministrationShell> inMemoryStore = new ConcurrentHashMap<>();

@Override
public <S extends AssetAdministrationShell> S save(S entity) {
public @NonNull <S extends AssetAdministrationShell> S save(@NonNull S entity) {
inMemoryStore.put(entity.getId(), entity);

return entity;
}

@Override
public <S extends AssetAdministrationShell> Iterable<S> saveAll(Iterable<S> entities) {
for (S entity : entities)
inMemoryStore.put(entity.getId(), entity);
public @NonNull <S extends AssetAdministrationShell> Iterable<S> saveAll(@NonNull Iterable<S> entities) {
entities.forEach(this::save);

return entities;
}

@Override
public Optional<AssetAdministrationShell> findById(String id) {
public @NonNull Optional<AssetAdministrationShell> findById(@NonNull String id) {
return Optional.ofNullable(inMemoryStore.get(id));
}

@Override
public boolean existsById(String id) {
public boolean existsById(@NonNull String id) {
return inMemoryStore.containsKey(id);
}

@Override
public Iterable<AssetAdministrationShell> findAll() {
public @NonNull Iterable<AssetAdministrationShell> findAll() {
return inMemoryStore.values();
}

@Override
public Iterable<AssetAdministrationShell> findAllById(Iterable<String> ids) {
public @NonNull Iterable<AssetAdministrationShell> findAllById(@NonNull Iterable<String> ids) {
return StreamSupport.stream(ids.spliterator(), false).map(inMemoryStore::get).filter(Objects::nonNull).collect(Collectors.toList());
}

Expand All @@ -86,23 +86,23 @@ public long count() {
}

@Override
public void deleteById(String id) {
public void deleteById(@NonNull String id) {
inMemoryStore.remove(id);
}

@Override
public void delete(AssetAdministrationShell entity) {
public void delete(@NonNull AssetAdministrationShell entity) {
inMemoryStore.remove(entity.getId());
}

@Override
public void deleteAllById(Iterable<? extends String> ids) {
public void deleteAllById(@NonNull Iterable<? extends String> ids) {
for (String id : ids)
inMemoryStore.remove(id);
}

@Override
public void deleteAll(Iterable<? extends AssetAdministrationShell> entities) {
public void deleteAll(@NonNull Iterable<? extends AssetAdministrationShell> entities) {
for (AssetAdministrationShell entity : entities)
inMemoryStore.remove(entity.getId());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,53 +25,55 @@

package org.eclipse.digitaltwin.basyx.aasxfileserver;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.eclipse.digitaltwin.basyx.aasxfileserver.model.Package;
import org.springframework.data.repository.CrudRepository;
import org.springframework.lang.NonNull;

public class AASXFileServerInMemoryCrudRepository implements CrudRepository<Package, String> {

private Map<String, Package> packageMap = new LinkedHashMap<>();
private final ConcurrentMap<String, Package> packageMap = new ConcurrentHashMap<>();

@Override
public <S extends Package> S save(S entity) {
public @NonNull <S extends Package> S save(@NonNull S entity) {
packageMap.put(entity.getPackageId(), entity);

return entity;
}

@Override
public <S extends Package> Iterable<S> saveAll(Iterable<S> entities) {
for (Package entity : entities) {
save(entity);
}
public @NonNull <S extends Package> Iterable<S> saveAll(@NonNull Iterable<S> entities) {
entities.forEach(this::save);

return entities;
}

@Override
public Optional<Package> findById(String id) {
public @NonNull Optional<Package> findById(@NonNull String id) {
return Optional.ofNullable(packageMap.get(id));
}

@Override
public boolean existsById(String id) {
public boolean existsById(@NonNull String id) {
return packageMap.containsKey(id);
}

@Override
public Iterable<Package> findAll() {
public @NonNull Iterable<Package> findAll() {
return packageMap.values();
}

@Override
public Iterable<Package> findAllById(Iterable<String> ids) {
public @NonNull Iterable<Package> findAllById(@NonNull Iterable<String> ids) {
List<String> idList = StreamSupport.stream(ids.spliterator(), false).collect(Collectors.toList());
return packageMap.entrySet().stream().filter(entry -> idList.contains(entry.getKey())).map(entry -> entry.getValue()).collect(Collectors.toList());
return packageMap.entrySet().stream().filter(entry -> idList.contains(entry.getKey())).map(Entry::getValue).collect(Collectors.toList());
}

@Override
Expand All @@ -80,23 +82,23 @@ public long count() {
}

@Override
public void deleteById(String id) {
public void deleteById(@NonNull String id) {
packageMap.remove(id);
}

@Override
public void delete(Package entity) {
public void delete(@NonNull Package entity) {
packageMap.remove(entity.getPackageId());
}

@Override
public void deleteAllById(Iterable<? extends String> ids) {
public void deleteAllById(@NonNull Iterable<? extends String> ids) {
List<String> idList = StreamSupport.stream(ids.spliterator(), false).collect(Collectors.toList());
packageMap.keySet().removeAll(idList);
}

@Override
public void deleteAll(Iterable<? extends Package> entities) {
public void deleteAll(@NonNull Iterable<? extends Package> entities) {
List<String> idList = StreamSupport.stream(entities.spliterator(), false).map(Package::getPackageId).collect(Collectors.toList());
packageMap.keySet().removeAll(idList);
}
Expand Down
Loading

0 comments on commit 68f1b42

Please sign in to comment.