|
|
|
@ -21,6 +21,11 @@ import com.google.common.util.concurrent.Futures; |
|
|
|
import com.google.common.util.concurrent.ListenableFuture; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
import org.springframework.cache.Cache; |
|
|
|
import org.springframework.cache.CacheManager; |
|
|
|
import org.springframework.cache.annotation.CacheEvict; |
|
|
|
import org.springframework.cache.annotation.Cacheable; |
|
|
|
import org.springframework.cache.annotation.Caching; |
|
|
|
import org.springframework.stereotype.Service; |
|
|
|
import org.springframework.util.StringUtils; |
|
|
|
import org.thingsboard.server.common.data.id.EntityId; |
|
|
|
@ -34,6 +39,8 @@ import java.util.concurrent.ConcurrentHashMap; |
|
|
|
import java.util.concurrent.ExecutionException; |
|
|
|
import java.util.function.BiConsumer; |
|
|
|
|
|
|
|
import static org.thingsboard.server.common.data.CacheConstants.RELATIONS_CACHE; |
|
|
|
|
|
|
|
/** |
|
|
|
* Created by ashvayka on 28.04.17. |
|
|
|
*/ |
|
|
|
@ -47,6 +54,9 @@ public class BaseRelationService implements RelationService { |
|
|
|
@Autowired |
|
|
|
private EntityService entityService; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private CacheManager cacheManager; |
|
|
|
|
|
|
|
@Override |
|
|
|
public ListenableFuture<Boolean> checkRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { |
|
|
|
log.trace("Executing checkRelation [{}][{}][{}][{}]", from, to, relationType, typeGroup); |
|
|
|
@ -54,6 +64,7 @@ public class BaseRelationService implements RelationService { |
|
|
|
return relationDao.checkRelation(from, to, relationType, typeGroup); |
|
|
|
} |
|
|
|
|
|
|
|
@Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType}") |
|
|
|
@Override |
|
|
|
public ListenableFuture<EntityRelation> getRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { |
|
|
|
log.trace("Executing EntityRelation [{}][{}][{}][{}]", from, to, relationType, typeGroup); |
|
|
|
@ -61,6 +72,12 @@ public class BaseRelationService implements RelationService { |
|
|
|
return relationDao.getRelation(from, to, relationType, typeGroup); |
|
|
|
} |
|
|
|
|
|
|
|
@Caching(evict = { |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.from"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type}"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.to"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type}") |
|
|
|
}) |
|
|
|
@Override |
|
|
|
public boolean saveRelation(EntityRelation relation) { |
|
|
|
log.trace("Executing saveRelation [{}]", relation); |
|
|
|
@ -68,6 +85,12 @@ public class BaseRelationService implements RelationService { |
|
|
|
return relationDao.saveRelation(relation); |
|
|
|
} |
|
|
|
|
|
|
|
@Caching(evict = { |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.from"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type}"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.to"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type}") |
|
|
|
}) |
|
|
|
@Override |
|
|
|
public ListenableFuture<Boolean> saveRelationAsync(EntityRelation relation) { |
|
|
|
log.trace("Executing saveRelationAsync [{}]", relation); |
|
|
|
@ -75,6 +98,13 @@ public class BaseRelationService implements RelationService { |
|
|
|
return relationDao.saveRelationAsync(relation); |
|
|
|
} |
|
|
|
|
|
|
|
@Caching(evict = { |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.from"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type}"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.to"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type}"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type}") |
|
|
|
}) |
|
|
|
@Override |
|
|
|
public boolean deleteRelation(EntityRelation relation) { |
|
|
|
log.trace("Executing deleteRelation [{}]", relation); |
|
|
|
@ -82,6 +112,13 @@ public class BaseRelationService implements RelationService { |
|
|
|
return relationDao.deleteRelation(relation); |
|
|
|
} |
|
|
|
|
|
|
|
@Caching(evict = { |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.from"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type}"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.to"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type}"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type}") |
|
|
|
}) |
|
|
|
@Override |
|
|
|
public ListenableFuture<Boolean> deleteRelationAsync(EntityRelation relation) { |
|
|
|
log.trace("Executing deleteRelationAsync [{}]", relation); |
|
|
|
@ -89,6 +126,13 @@ public class BaseRelationService implements RelationService { |
|
|
|
return relationDao.deleteRelationAsync(relation); |
|
|
|
} |
|
|
|
|
|
|
|
@Caching(evict = { |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "#from"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType}"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "#to"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType}"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType}") |
|
|
|
}) |
|
|
|
@Override |
|
|
|
public boolean deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { |
|
|
|
log.trace("Executing deleteRelation [{}][{}][{}][{}]", from, to, relationType, typeGroup); |
|
|
|
@ -96,6 +140,13 @@ public class BaseRelationService implements RelationService { |
|
|
|
return relationDao.deleteRelation(from, to, relationType, typeGroup); |
|
|
|
} |
|
|
|
|
|
|
|
@Caching(evict = { |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "#from"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType}"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "#to"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType}"), |
|
|
|
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType}") |
|
|
|
}) |
|
|
|
@Override |
|
|
|
public ListenableFuture<Boolean> deleteRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { |
|
|
|
log.trace("Executing deleteRelationAsync [{}][{}][{}][{}]", from, to, relationType, typeGroup); |
|
|
|
@ -105,23 +156,17 @@ public class BaseRelationService implements RelationService { |
|
|
|
|
|
|
|
@Override |
|
|
|
public boolean deleteEntityRelations(EntityId entity) { |
|
|
|
Cache cache = cacheManager.getCache(RELATIONS_CACHE); |
|
|
|
log.trace("Executing deleteEntityRelations [{}]", entity); |
|
|
|
validate(entity); |
|
|
|
List<ListenableFuture<List<EntityRelation>>> inboundRelationsList = new ArrayList<>(); |
|
|
|
List<ListenableFuture<List<EntityRelation>>> inboundRelationsListTo = new ArrayList<>(); |
|
|
|
for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { |
|
|
|
inboundRelationsList.add(relationDao.findAllByTo(entity, typeGroup)); |
|
|
|
inboundRelationsListTo.add(relationDao.findAllByTo(entity, typeGroup)); |
|
|
|
} |
|
|
|
ListenableFuture<List<List<EntityRelation>>> inboundRelations = Futures.allAsList(inboundRelationsList); |
|
|
|
ListenableFuture<List<Boolean>> inboundDeletions = Futures.transform(inboundRelations, new Function<List<List<EntityRelation>>, List<Boolean>>() { |
|
|
|
@Override |
|
|
|
public List<Boolean> apply(List<List<EntityRelation>> relations) { |
|
|
|
List<Boolean> results = new ArrayList<>(); |
|
|
|
for (List<EntityRelation> relationList : relations) { |
|
|
|
relationList.stream().forEach(relation -> results.add(relationDao.deleteRelation(relation))); |
|
|
|
} |
|
|
|
return results; |
|
|
|
} |
|
|
|
}); |
|
|
|
ListenableFuture<List<List<EntityRelation>>> inboundRelationsTo = Futures.allAsList(inboundRelationsListTo); |
|
|
|
ListenableFuture<List<Boolean>> inboundDeletions = Futures.transform(inboundRelationsTo, (List<List<EntityRelation>> relations) -> |
|
|
|
getBooleans(relations, cache, true)); |
|
|
|
|
|
|
|
ListenableFuture<Boolean> inboundFuture = Futures.transform(inboundDeletions, getListToBooleanFunction()); |
|
|
|
boolean inboundDeleteResult = false; |
|
|
|
try { |
|
|
|
@ -129,37 +174,105 @@ public class BaseRelationService implements RelationService { |
|
|
|
} catch (InterruptedException | ExecutionException e) { |
|
|
|
log.error("Error deleting entity inbound relations", e); |
|
|
|
} |
|
|
|
|
|
|
|
List<ListenableFuture<List<EntityRelation>>> inboundRelationsListFrom = new ArrayList<>(); |
|
|
|
for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { |
|
|
|
inboundRelationsListFrom.add(relationDao.findAllByFrom(entity, typeGroup)); |
|
|
|
} |
|
|
|
ListenableFuture<List<List<EntityRelation>>> inboundRelationsFrom = Futures.allAsList(inboundRelationsListFrom); |
|
|
|
Futures.transform(inboundRelationsFrom, (Function<List<List<EntityRelation>>, List<Boolean>>) relations -> |
|
|
|
getBooleans(relations, cache, false)); |
|
|
|
|
|
|
|
boolean outboundDeleteResult = relationDao.deleteOutboundRelations(entity); |
|
|
|
return inboundDeleteResult && outboundDeleteResult; |
|
|
|
} |
|
|
|
|
|
|
|
private List<Boolean> getBooleans(List<List<EntityRelation>> relations, Cache cache, boolean isRemove) { |
|
|
|
List<Boolean> results = new ArrayList<>(); |
|
|
|
for (List<EntityRelation> relationList : relations) { |
|
|
|
relationList.stream().forEach(relation -> { |
|
|
|
checkFromDeleteSync(cache, results, relation, isRemove); |
|
|
|
}); |
|
|
|
} |
|
|
|
return results; |
|
|
|
} |
|
|
|
|
|
|
|
private void checkFromDeleteSync(Cache cache, List<Boolean> results, EntityRelation relation, boolean isRemove) { |
|
|
|
if (isRemove) { |
|
|
|
results.add(relationDao.deleteRelation(relation)); |
|
|
|
cacheEviction(relation, relation.getTo(), cache); |
|
|
|
} else { |
|
|
|
cacheEviction(relation, relation.getFrom(), cache); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public ListenableFuture<Boolean> deleteEntityRelationsAsync(EntityId entity) { |
|
|
|
Cache cache = cacheManager.getCache(RELATIONS_CACHE); |
|
|
|
log.trace("Executing deleteEntityRelationsAsync [{}]", entity); |
|
|
|
validate(entity); |
|
|
|
List<ListenableFuture<List<EntityRelation>>> inboundRelationsList = new ArrayList<>(); |
|
|
|
List<ListenableFuture<List<EntityRelation>>> inboundRelationsListTo = new ArrayList<>(); |
|
|
|
for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { |
|
|
|
inboundRelationsList.add(relationDao.findAllByTo(entity, typeGroup)); |
|
|
|
inboundRelationsListTo.add(relationDao.findAllByTo(entity, typeGroup)); |
|
|
|
} |
|
|
|
ListenableFuture<List<List<EntityRelation>>> inboundRelations = Futures.allAsList(inboundRelationsList); |
|
|
|
ListenableFuture<List<Boolean>> inboundDeletions = Futures.transform(inboundRelations, new AsyncFunction<List<List<EntityRelation>>, List<Boolean>>() { |
|
|
|
@Override |
|
|
|
public ListenableFuture<List<Boolean>> apply(List<List<EntityRelation>> relations) throws Exception { |
|
|
|
List<ListenableFuture<Boolean>> results = new ArrayList<>(); |
|
|
|
for (List<EntityRelation> relationList : relations) { |
|
|
|
relationList.stream().forEach(relation -> results.add(relationDao.deleteRelationAsync(relation))); |
|
|
|
} |
|
|
|
return Futures.allAsList(results); |
|
|
|
} |
|
|
|
ListenableFuture<List<List<EntityRelation>>> inboundRelationsTo = Futures.allAsList(inboundRelationsListTo); |
|
|
|
ListenableFuture<List<Boolean>> inboundDeletions = Futures.transform(inboundRelationsTo, |
|
|
|
(AsyncFunction<List<List<EntityRelation>>, List<Boolean>>) relations -> { |
|
|
|
List<ListenableFuture<Boolean>> results = getListenableFutures(relations, cache, true); |
|
|
|
return Futures.allAsList(results); |
|
|
|
}); |
|
|
|
|
|
|
|
ListenableFuture<Boolean> inboundFuture = Futures.transform(inboundDeletions, getListToBooleanFunction()); |
|
|
|
|
|
|
|
ListenableFuture<Boolean> outboundFuture = relationDao.deleteOutboundRelationsAsync(entity); |
|
|
|
List<ListenableFuture<List<EntityRelation>>> inboundRelationsListFrom = new ArrayList<>(); |
|
|
|
for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { |
|
|
|
inboundRelationsListFrom.add(relationDao.findAllByTo(entity, typeGroup)); |
|
|
|
} |
|
|
|
ListenableFuture<List<List<EntityRelation>>> inboundRelationsFrom = Futures.allAsList(inboundRelationsListFrom); |
|
|
|
Futures.transform(inboundRelationsFrom, (AsyncFunction<List<List<EntityRelation>>, List<Boolean>>) relations -> { |
|
|
|
List<ListenableFuture<Boolean>> results = getListenableFutures(relations, cache, false); |
|
|
|
return Futures.allAsList(results); |
|
|
|
}); |
|
|
|
|
|
|
|
ListenableFuture<Boolean> outboundFuture = relationDao.deleteOutboundRelationsAsync(entity); |
|
|
|
return Futures.transform(Futures.allAsList(Arrays.asList(inboundFuture, outboundFuture)), getListToBooleanFunction()); |
|
|
|
} |
|
|
|
|
|
|
|
private List<ListenableFuture<Boolean>> getListenableFutures(List<List<EntityRelation>> relations, Cache cache, boolean isRemove) { |
|
|
|
List<ListenableFuture<Boolean>> results = new ArrayList<>(); |
|
|
|
for (List<EntityRelation> relationList : relations) { |
|
|
|
relationList.stream().forEach(relation -> { |
|
|
|
checkFromDeleteAsync(cache, results, relation, isRemove); |
|
|
|
}); |
|
|
|
} |
|
|
|
return results; |
|
|
|
} |
|
|
|
|
|
|
|
private void checkFromDeleteAsync(Cache cache, List<ListenableFuture<Boolean>> results, EntityRelation relation, boolean isRemove) { |
|
|
|
if (isRemove) { |
|
|
|
results.add(relationDao.deleteRelationAsync(relation)); |
|
|
|
cacheEviction(relation, relation.getTo(), cache); |
|
|
|
} else { |
|
|
|
cacheEviction(relation, relation.getFrom(), cache); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void cacheEviction(EntityRelation relation, EntityId entityId, Cache cache) { |
|
|
|
cache.evict(entityId); |
|
|
|
|
|
|
|
List<Object> toAndType = new ArrayList<>(); |
|
|
|
toAndType.add(entityId); |
|
|
|
toAndType.add(relation.getType()); |
|
|
|
cache.evict(toAndType); |
|
|
|
|
|
|
|
List<Object> fromToAndType = new ArrayList<>(); |
|
|
|
fromToAndType.add(relation.getFrom()); |
|
|
|
fromToAndType.add(relation.getTo()); |
|
|
|
fromToAndType.add(relation.getType()); |
|
|
|
cache.evict(fromToAndType); |
|
|
|
} |
|
|
|
|
|
|
|
@Cacheable(cacheNames = RELATIONS_CACHE, key = "#from") |
|
|
|
@Override |
|
|
|
public ListenableFuture<List<EntityRelation>> findByFrom(EntityId from, RelationTypeGroup typeGroup) { |
|
|
|
log.trace("Executing findByFrom [{}][{}]", from, typeGroup); |
|
|
|
@ -176,17 +289,18 @@ public class BaseRelationService implements RelationService { |
|
|
|
ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByFrom(from, typeGroup); |
|
|
|
ListenableFuture<List<EntityRelationInfo>> relationsInfo = Futures.transform(relations, |
|
|
|
(AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> { |
|
|
|
List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); |
|
|
|
List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); |
|
|
|
relations1.stream().forEach(relation -> |
|
|
|
futures.add(fetchRelationInfoAsync(relation, |
|
|
|
relation2 -> relation2.getTo(), |
|
|
|
(EntityRelationInfo relationInfo, String entityName) -> relationInfo.setToName(entityName))) |
|
|
|
); |
|
|
|
return Futures.successfulAsList(futures); |
|
|
|
}); |
|
|
|
}); |
|
|
|
return relationsInfo; |
|
|
|
} |
|
|
|
|
|
|
|
@Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType}") |
|
|
|
@Override |
|
|
|
public ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) { |
|
|
|
log.trace("Executing findByFromAndType [{}][{}][{}]", from, relationType, typeGroup); |
|
|
|
@ -196,6 +310,7 @@ public class BaseRelationService implements RelationService { |
|
|
|
return relationDao.findAllByFromAndType(from, relationType, typeGroup); |
|
|
|
} |
|
|
|
|
|
|
|
@Cacheable(cacheNames = RELATIONS_CACHE, key = "#to") |
|
|
|
@Override |
|
|
|
public ListenableFuture<List<EntityRelation>> findByTo(EntityId to, RelationTypeGroup typeGroup) { |
|
|
|
log.trace("Executing findByTo [{}][{}]", to, typeGroup); |
|
|
|
@ -214,9 +329,9 @@ public class BaseRelationService implements RelationService { |
|
|
|
(AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> { |
|
|
|
List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); |
|
|
|
relations1.stream().forEach(relation -> |
|
|
|
futures.add(fetchRelationInfoAsync(relation, |
|
|
|
relation2 -> relation2.getFrom(), |
|
|
|
(EntityRelationInfo relationInfo, String entityName) -> relationInfo.setFromName(entityName))) |
|
|
|
futures.add(fetchRelationInfoAsync(relation, |
|
|
|
relation2 -> relation2.getFrom(), |
|
|
|
(EntityRelationInfo relationInfo, String entityName) -> relationInfo.setFromName(entityName))) |
|
|
|
); |
|
|
|
return Futures.successfulAsList(futures); |
|
|
|
}); |
|
|
|
@ -236,6 +351,7 @@ public class BaseRelationService implements RelationService { |
|
|
|
return entityRelationInfo; |
|
|
|
} |
|
|
|
|
|
|
|
@Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType}") |
|
|
|
@Override |
|
|
|
public ListenableFuture<List<EntityRelation>> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) { |
|
|
|
log.trace("Executing findByToAndType [{}][{}][{}]", to, relationType, typeGroup); |
|
|
|
@ -417,5 +533,4 @@ public class BaseRelationService implements RelationService { |
|
|
|
} |
|
|
|
return relations; |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|