From dd580021587667fe3c3eca312c57ff66f10356f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Voz=C3=A1r?= Date: Mon, 1 Sep 2025 09:08:26 +0200 Subject: [PATCH 01/40] NAE-2182 - Implementation of an endpoint for clearing the action cache --- .../FieldActionCacheEndpoint.java | 26 +++++++++++++++++++ .../src/main/resources/application.yaml | 1 + 2 files changed, 27 insertions(+) create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/FieldActionCacheEndpoint.java diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/FieldActionCacheEndpoint.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/FieldActionCacheEndpoint.java new file mode 100644 index 0000000000..2edfee2adb --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/FieldActionCacheEndpoint.java @@ -0,0 +1,26 @@ +package com.netgrif.application.engine.configuration; + +import com.netgrif.application.engine.workflow.service.FieldActionsCacheService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@Endpoint(id = "fieldActions") +public class FieldActionCacheEndpoint { + + private final FieldActionsCacheService cacheService; + + public FieldActionCacheEndpoint(FieldActionsCacheService cacheService) { + this.cacheService = cacheService; + } + + @DeleteOperation + public String clear() { + cacheService.clearActionCache(); + log.info("actionsCache cleared"); + return "actionsCache cleared"; + } +} diff --git a/application-engine/src/main/resources/application.yaml b/application-engine/src/main/resources/application.yaml index 4e2bb303c1..93e9681bf5 100644 --- a/application-engine/src/main/resources/application.yaml +++ b/application-engine/src/main/resources/application.yaml @@ -117,6 +117,7 @@ netgrif: prometheus: metric exposure: exclude: shutdown + include: fieldActions endpoint: health: show-details: when_authorized From 5b98ca49984500cd59fe7e37d8beff0309491f52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Voz=C3=A1r?= Date: Mon, 1 Sep 2025 15:10:05 +0200 Subject: [PATCH 02/40] NAE-2182 - Implementation of custom cache wrapper to use in cacheManager --- .../configuration/ActionsCacheWrapper.java | 54 +++++++++++++++++++ .../configuration/CacheConfiguration.java | 24 ++++++++- 2 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsCacheWrapper.java diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsCacheWrapper.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsCacheWrapper.java new file mode 100644 index 0000000000..73e1a8c1c4 --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsCacheWrapper.java @@ -0,0 +1,54 @@ +package com.netgrif.application.engine.configuration; + +import com.netgrif.application.engine.workflow.service.FieldActionsCacheService; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.cache.Cache; + +import java.util.concurrent.Callable; + +@Slf4j +public class ActionsCacheWrapper implements Cache { + private final Cache cache; + private final FieldActionsCacheService fieldActionsCacheService; + + + public ActionsCacheWrapper(Cache cache, FieldActionsCacheService fieldActionsCacheService) { + this.cache = cache; + this.fieldActionsCacheService = fieldActionsCacheService; + } + @NotNull + @Override + public String getName() { + return cache.getName(); + } + @NotNull + @Override + public Object getNativeCache() { + return cache.getNativeCache(); + } + @NotNull + @Override public ValueWrapper get(@NotNull Object key) { + return cache.get(key); + } + @NotNull + @Override public T get(@NotNull Object key, @NotNull Class type) { + return cache.get(key, type); + } + @NotNull + @Override public T get(@NotNull Object key, @NotNull Callable loader) { + return cache.get(key, loader); + } + @Override public void put(@NotNull Object key, @NotNull Object value) { + cache.put(key, value); + } + @Override public void evict(@NotNull Object key) { + cache.evict(key); + } + + @Override public void clear() { + cache.clear(); + log.info("actionsCache cleared from cache actuator"); + fieldActionsCacheService.clearActionCache(); + } +} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java index 1e0ad25c87..709067d68a 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java @@ -1,30 +1,50 @@ package com.netgrif.application.engine.configuration; import com.netgrif.application.engine.configuration.properties.CacheConfigurationProperties; +import com.netgrif.application.engine.workflow.service.FieldActionsCacheService; +import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.concurrent.ConcurrentMapCache; import org.springframework.cache.concurrent.ConcurrentMapCacheManager; import org.springframework.cache.interceptor.CacheResolver; +import org.springframework.cache.support.SimpleCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; +import java.lang.reflect.Array; +import java.util.*; +import java.util.stream.Collectors; + @Configuration @EnableCaching public class CacheConfiguration extends CachingConfigurerSupport { private final CacheConfigurationProperties properties; + private final FieldActionsCacheService fieldActionsCacheService; - public CacheConfiguration(CacheConfigurationProperties properties) { + public CacheConfiguration(CacheConfigurationProperties properties, FieldActionsCacheService fieldActionsCacheService) { this.properties = properties; + this.fieldActionsCacheService = fieldActionsCacheService; } @Bean @Primary @Override public CacheManager cacheManager() { - return new ConcurrentMapCacheManager(properties.getAllCaches().toArray(String[]::new)); + Set cacheNames = properties.getAllCaches(); + + List caches = cacheNames.stream() + .map(ConcurrentMapCache::new) + .collect(Collectors.toCollection(ArrayList::new)); + + caches.add(new ActionsCacheWrapper(new ConcurrentMapCache("actionsCache"), fieldActionsCacheService)); + + SimpleCacheManager cacheManager = new SimpleCacheManager(); + cacheManager.setCaches(caches); + return cacheManager; } @Bean From e917f763c4e4bab03fb94735136fe2128c3ef85f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Voz=C3=A1r?= Date: Tue, 2 Sep 2025 13:41:13 +0200 Subject: [PATCH 03/40] NAE-2182 - Implementation of generic map cache and created actuator cache endpoints for actionsCache, functionsCache and namespaceFunctionsCache --- .../configuration/ActionsCacheWrapper.java | 54 ------------- .../configuration/CacheConfiguration.java | 40 +++++++--- .../engine/configuration/CacheMapKeys.java | 7 ++ .../FieldActionCacheEndpoint.java | 26 ------- .../engine/configuration/GenericMapCache.java | 78 +++++++++++++++++++ .../service/FieldActionsCacheService.java | 66 ++++++++-------- .../src/main/resources/application.yaml | 2 +- 7 files changed, 152 insertions(+), 121 deletions(-) delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsCacheWrapper.java create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/FieldActionCacheEndpoint.java create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsCacheWrapper.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsCacheWrapper.java deleted file mode 100644 index 73e1a8c1c4..0000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsCacheWrapper.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.netgrif.application.engine.configuration; - -import com.netgrif.application.engine.workflow.service.FieldActionsCacheService; -import lombok.extern.slf4j.Slf4j; -import org.jetbrains.annotations.NotNull; -import org.springframework.cache.Cache; - -import java.util.concurrent.Callable; - -@Slf4j -public class ActionsCacheWrapper implements Cache { - private final Cache cache; - private final FieldActionsCacheService fieldActionsCacheService; - - - public ActionsCacheWrapper(Cache cache, FieldActionsCacheService fieldActionsCacheService) { - this.cache = cache; - this.fieldActionsCacheService = fieldActionsCacheService; - } - @NotNull - @Override - public String getName() { - return cache.getName(); - } - @NotNull - @Override - public Object getNativeCache() { - return cache.getNativeCache(); - } - @NotNull - @Override public ValueWrapper get(@NotNull Object key) { - return cache.get(key); - } - @NotNull - @Override public T get(@NotNull Object key, @NotNull Class type) { - return cache.get(key, type); - } - @NotNull - @Override public T get(@NotNull Object key, @NotNull Callable loader) { - return cache.get(key, loader); - } - @Override public void put(@NotNull Object key, @NotNull Object value) { - cache.put(key, value); - } - @Override public void evict(@NotNull Object key) { - cache.evict(key); - } - - @Override public void clear() { - cache.clear(); - log.info("actionsCache cleared from cache actuator"); - fieldActionsCacheService.clearActionCache(); - } -} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java index 709067d68a..33de904b44 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java @@ -1,33 +1,33 @@ package com.netgrif.application.engine.configuration; import com.netgrif.application.engine.configuration.properties.CacheConfigurationProperties; -import com.netgrif.application.engine.workflow.service.FieldActionsCacheService; +import com.netgrif.application.engine.configuration.properties.RunnerConfigurationProperties; +import com.netgrif.application.engine.elastic.service.executors.MaxSizeHashMap; +import com.netgrif.application.engine.workflow.domain.CachedFunction; +import groovy.lang.Closure; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.concurrent.ConcurrentMapCache; -import org.springframework.cache.concurrent.ConcurrentMapCacheManager; import org.springframework.cache.interceptor.CacheResolver; import org.springframework.cache.support.SimpleCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; -import java.lang.reflect.Array; import java.util.*; import java.util.stream.Collectors; @Configuration @EnableCaching public class CacheConfiguration extends CachingConfigurerSupport { - + private final RunnerConfigurationProperties.FieldRunnerProperties fieldRunnerProperties; private final CacheConfigurationProperties properties; - private final FieldActionsCacheService fieldActionsCacheService; - public CacheConfiguration(CacheConfigurationProperties properties, FieldActionsCacheService fieldActionsCacheService) { + public CacheConfiguration(RunnerConfigurationProperties.FieldRunnerProperties fieldRunnerProperties, CacheConfigurationProperties properties) { + this.fieldRunnerProperties = fieldRunnerProperties; this.properties = properties; - this.fieldActionsCacheService = fieldActionsCacheService; } @Bean @@ -35,12 +35,34 @@ public CacheConfiguration(CacheConfigurationProperties properties, FieldActionsC @Override public CacheManager cacheManager() { Set cacheNames = properties.getAllCaches(); - List caches = cacheNames.stream() .map(ConcurrentMapCache::new) .collect(Collectors.toCollection(ArrayList::new)); - caches.add(new ActionsCacheWrapper(new ConcurrentMapCache("actionsCache"), fieldActionsCacheService)); + + java.util.function.Supplier> actionsFactory = () -> new MaxSizeHashMap<>(fieldRunnerProperties.getActionCacheSize()); + + caches.add(new GenericMapCache<>( + CacheMapKeys.ACTIONS, + Closure.class, + actionsFactory + )); + + java.util.function.Supplier> functionsFactory = () -> new MaxSizeHashMap<>(fieldRunnerProperties.getFunctionsCacheSize()); + + caches.add(new GenericMapCache<>( + CacheMapKeys.FUNCTIONS, + CachedFunction.class, + functionsFactory + )); + + java.util.function.Supplier>> nsFactory = () -> new MaxSizeHashMap<>(fieldRunnerProperties.getNamespaceCacheSize()); + + caches.add(new GenericMapCache<>( + CacheMapKeys.NAMESPACE_FUNCTIONS, + List.class, + nsFactory + )); SimpleCacheManager cacheManager = new SimpleCacheManager(); cacheManager.setCaches(caches); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java new file mode 100644 index 0000000000..8928b0f43b --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java @@ -0,0 +1,7 @@ +package com.netgrif.application.engine.configuration; + +public class CacheMapKeys { + public static final String ACTIONS = "actionsCache"; + public static final String FUNCTIONS = "functionsCache"; + public static final String NAMESPACE_FUNCTIONS = "namespaceFunctionsCache"; +} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/FieldActionCacheEndpoint.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/FieldActionCacheEndpoint.java deleted file mode 100644 index 2edfee2adb..0000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/FieldActionCacheEndpoint.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.netgrif.application.engine.configuration; - -import com.netgrif.application.engine.workflow.service.FieldActionsCacheService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation; -import org.springframework.boot.actuate.endpoint.annotation.Endpoint; -import org.springframework.stereotype.Component; - -@Slf4j -@Component -@Endpoint(id = "fieldActions") -public class FieldActionCacheEndpoint { - - private final FieldActionsCacheService cacheService; - - public FieldActionCacheEndpoint(FieldActionsCacheService cacheService) { - this.cacheService = cacheService; - } - - @DeleteOperation - public String clear() { - cacheService.clearActionCache(); - log.info("actionsCache cleared"); - return "actionsCache cleared"; - } -} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java new file mode 100644 index 0000000000..1c53f632fb --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java @@ -0,0 +1,78 @@ +package com.netgrif.application.engine.configuration; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.Cache; + +import java.util.Map; + +@Slf4j +public class GenericMapCache implements Cache { + private final String name; + protected Class valueType; + private final java.util.function.Supplier> mapFactory; + private volatile Map map; + + public GenericMapCache(String name, Class valueType, java.util.function.Supplier> mapFactory) { + this.name = name; + this.valueType = valueType; + this.mapFactory = mapFactory; + this.map = mapFactory.get(); + } + + @Override public String getName() { return name; } + @Override public Object getNativeCache() { return map; } + + @Override public ValueWrapper get(Object key) { + Object valueObject = map.get(String.valueOf(key)); + return (valueObject == null) ? null : new org.springframework.cache.support.SimpleValueWrapper(valueObject); + } + + @Override public T get(Object key, Class type) { + Object valueObject = map.get(String.valueOf(key)); + return (valueObject == null) ? null : type.cast(valueObject); + } + + @Override public T get(Object key, java.util.concurrent.Callable loader) { + String stringKey = String.valueOf(key); + Object present = map.get(stringKey); + + if (present != null) { + return (T) present; + } + + try { + T computed = loader.call(); + if (computed != null) { + map.put(stringKey, safeCast(computed)); + } + return computed; + } catch (Exception ex) { + throw new org.springframework.cache.Cache.ValueRetrievalException(stringKey, loader, ex); + } + } + + @Override public void put(Object key, Object value) { + map.put(String.valueOf(key), safeCast(value)); + } + + @Override public void evict(Object key) { + map.remove(String.valueOf(key)); + } + + @Override public void clear() { + this.map = mapFactory.get(); + log.info("{} cache cleared", this.getName()); + } + + protected V safeCast(Object object) { + if (object == null) { + return null; + } + + if (!valueType.isInstance(object)) { + throw new ClassCastException("Expected " + valueType.getName() + " but was " + object.getClass().getName()); + } + + return (V) object; + } +} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java index 1b996bc60d..ddb8d80b30 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java @@ -1,7 +1,6 @@ package com.netgrif.application.engine.workflow.service; -import com.netgrif.application.engine.configuration.properties.RunnerConfigurationProperties; -import com.netgrif.application.engine.elastic.service.executors.MaxSizeHashMap; +import com.netgrif.application.engine.configuration.CacheMapKeys; import com.netgrif.application.engine.event.IGroovyShellFactory; import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; import com.netgrif.application.engine.objects.petrinet.domain.Function; @@ -14,33 +13,26 @@ import groovy.lang.GroovyShell; 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.context.annotation.Lazy; import org.springframework.stereotype.Service; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; @Service @Slf4j public class FieldActionsCacheService implements IFieldActionsCacheService { - private final RunnerConfigurationProperties.FieldRunnerProperties properties; + private final CacheManager cacheManager; private IPetriNetService petriNetService; - private Map actionsCache; - private Map> namespaceFunctionsCache; - private Map functionsCache; private final GroovyShell shell; - public FieldActionsCacheService(RunnerConfigurationProperties.FieldRunnerProperties properties, IGroovyShellFactory shellFactory) { - this.properties = properties; - this.actionsCache = new MaxSizeHashMap<>(properties.getActionCacheSize()); - this.functionsCache = new MaxSizeHashMap<>(properties.getFunctionsCacheSize()); - this.namespaceFunctionsCache = new MaxSizeHashMap<>(properties.getNamespaceCacheSize()); + public FieldActionsCacheService(CacheManager cacheManager, IGroovyShellFactory shellFactory) { + this.cacheManager = cacheManager; this.shell = shellFactory.getGroovyShell(); } @@ -60,39 +52,51 @@ public void cachePetriNetFunctions(PetriNet petriNet) { .map(function -> CachedFunction.build(shell, function)) .collect(Collectors.toList()); + Cache namespaceFunctionsCache = cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS); + if (!functions.isEmpty()) { evaluateCachedFunctions(functions); namespaceFunctionsCache.put(petriNet.getIdentifier(), functions); } else { - namespaceFunctionsCache.remove(petriNet.getIdentifier()); + namespaceFunctionsCache.evictIfPresent(petriNet.getIdentifier()); } } @Override public void reloadCachedFunctions(PetriNet petriNet) { - namespaceFunctionsCache.remove(petriNet.getIdentifier()); + cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS).evictIfPresent(petriNet.getIdentifier()); cachePetriNetFunctions(petriNetService.getNewestVersionByIdentifier(petriNet.getIdentifier())); } @Override public Closure getCompiledAction(Action action, boolean shouldRewriteCachedActions) { String stringId = action.getId().toString(); - if (shouldRewriteCachedActions || !actionsCache.containsKey(stringId)) { - Closure code = (Closure) shell.evaluate("{-> " + action.getDefinition() + "}"); - actionsCache.put(stringId, code); + Cache actionsCache = cacheManager.getCache(CacheMapKeys.ACTIONS); + Object nativeActionsCache = actionsCache.getNativeCache(); + + if (nativeActionsCache instanceof Map map) { + if (shouldRewriteCachedActions || map.containsKey(stringId) ) { + Closure code = (Closure) shell.evaluate("{-> " + action.getDefinition() + "}"); + actionsCache.put(stringId, code); + } } - return actionsCache.get(stringId); + return (Closure) actionsCache.get(stringId).get(); } @Override public List getCachedFunctions(List functions) { List cachedFunctions = new ArrayList<>(functions.size()); - functions.forEach(function -> { - if (!functionsCache.containsKey(function.getStringId())) { - functionsCache.put(function.getStringId(), CachedFunction.build(shell, function)); - } - cachedFunctions.add(functionsCache.get(function.getStringId())); - }); + Cache functionsCache = cacheManager.getCache(CacheMapKeys.FUNCTIONS); + Object nativeFunctionsCache = functionsCache.getNativeCache(); + + if (nativeFunctionsCache instanceof Map map) { + functions.forEach(function -> { + if (!map.containsKey(function.getStringId())) { + functionsCache.put(function.getStringId(), CachedFunction.build(shell, function)); + } + cachedFunctions.add((CachedFunction) functionsCache.get(function.getStringId()).get()); + }); + } return cachedFunctions; } @@ -135,21 +139,21 @@ private String stringifyParameterTypes(Class[] a) { @Override public Map> getNamespaceFunctionCache() { - return new HashMap<>(namespaceFunctionsCache); + return new HashMap<>((Map) cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS).getNativeCache()); } @Override public void clearActionCache() { - this.actionsCache = new MaxSizeHashMap<>(properties.getActionCacheSize()); + cacheManager.getCache(CacheMapKeys.ACTIONS).clear(); } @Override public void clearNamespaceFunctionCache() { - this.namespaceFunctionsCache = new MaxSizeHashMap<>(properties.getNamespaceCacheSize()); + cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS).clear(); } @Override public void clearFunctionCache() { - this.functionsCache = new MaxSizeHashMap<>(properties.getFunctionsCacheSize()); + cacheManager.getCache(CacheMapKeys.FUNCTIONS).clear(); } } diff --git a/application-engine/src/main/resources/application.yaml b/application-engine/src/main/resources/application.yaml index 93e9681bf5..da22653c8a 100644 --- a/application-engine/src/main/resources/application.yaml +++ b/application-engine/src/main/resources/application.yaml @@ -117,7 +117,7 @@ netgrif: prometheus: metric exposure: exclude: shutdown - include: fieldActions + include: "*" endpoint: health: show-details: when_authorized From bd719b580c37af76fbe600efb110fe8562e03801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Voz=C3=A1r?= Date: Wed, 3 Sep 2025 20:08:20 +0200 Subject: [PATCH 04/40] NAE-2182 - cache FunctionsNamespace and functions if not found --- .../engine/configuration/ActionsMapCache.java | 41 +++++++++++++++ .../configuration/CacheConfiguration.java | 38 +++++++++----- .../configuration/FunctionsMapCache.java | 52 +++++++++++++++++++ .../FunctionsNamespaceMapCache.java | 45 ++++++++++++++++ .../engine/configuration/GenericMapCache.java | 36 +++++++------ .../petrinet/service/PetriNetService.java | 19 +++++-- .../service/interfaces/IPetriNetService.java | 16 ++++-- .../service/FieldActionsCacheService.java | 13 +++-- .../interfaces/IFieldActionsCacheService.java | 2 + 9 files changed, 220 insertions(+), 42 deletions(-) create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java new file mode 100644 index 0000000000..c2701c6a43 --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java @@ -0,0 +1,41 @@ +package com.netgrif.application.engine.configuration; + +import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; +import com.netgrif.application.engine.workflow.service.interfaces.IFieldActionsCacheService; +import groovy.lang.Closure; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; + +@Slf4j +public class ActionsMapCache extends GenericMapCache { + + public ActionsMapCache(String name, java.util.function.Supplier> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService) { + super(name, Closure.class, mapFactory, fieldActionsCacheService, petriNetService); + } + + @Override + public ValueWrapper get(Object key) { + String stringKey = String.valueOf(key); + + Object valueObject = map.get(stringKey); + if (valueObject != null) { + return new org.springframework.cache.support.SimpleValueWrapper(valueObject); + } + fieldActionsCacheService.reloadCachedFunctions(stringKey); + return new org.springframework.cache.support.SimpleValueWrapper(map.get(stringKey)); + } + + public T get(Object key, Class type) { + String stringKey = String.valueOf(key); + Object valueObject = map.get(stringKey); + + if (valueObject != null) { + return type.cast(valueObject); + } + + fieldActionsCacheService.reloadCachedFunctions(stringKey); + return type.cast(map.get(stringKey)); + + } +} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java index 33de904b44..db26422602 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java @@ -3,7 +3,9 @@ import com.netgrif.application.engine.configuration.properties.CacheConfigurationProperties; import com.netgrif.application.engine.configuration.properties.RunnerConfigurationProperties; import com.netgrif.application.engine.elastic.service.executors.MaxSizeHashMap; +import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; import com.netgrif.application.engine.workflow.domain.CachedFunction; +import com.netgrif.application.engine.workflow.service.interfaces.IFieldActionsCacheService; import groovy.lang.Closure; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; @@ -17,6 +19,7 @@ import org.springframework.context.annotation.Primary; import java.util.*; +import java.util.function.Supplier; import java.util.stream.Collectors; @Configuration @@ -24,10 +27,16 @@ public class CacheConfiguration extends CachingConfigurerSupport { private final RunnerConfigurationProperties.FieldRunnerProperties fieldRunnerProperties; private final CacheConfigurationProperties properties; + private final IFieldActionsCacheService fieldActionsCacheService; - public CacheConfiguration(RunnerConfigurationProperties.FieldRunnerProperties fieldRunnerProperties, CacheConfigurationProperties properties) { + private final IPetriNetService petriNetService; + + + public CacheConfiguration(RunnerConfigurationProperties.FieldRunnerProperties fieldRunnerProperties, CacheConfigurationProperties properties, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService) { this.fieldRunnerProperties = fieldRunnerProperties; this.properties = properties; + this.fieldActionsCacheService = fieldActionsCacheService; + this.petriNetService = petriNetService; } @Bean @@ -40,28 +49,31 @@ public CacheManager cacheManager() { .collect(Collectors.toCollection(ArrayList::new)); - java.util.function.Supplier> actionsFactory = () -> new MaxSizeHashMap<>(fieldRunnerProperties.getActionCacheSize()); + Supplier> actionsFactory = () -> new MaxSizeHashMap<>(fieldRunnerProperties.getActionCacheSize()); - caches.add(new GenericMapCache<>( + caches.add(new ActionsMapCache( CacheMapKeys.ACTIONS, - Closure.class, - actionsFactory + actionsFactory, + fieldActionsCacheService, + petriNetService )); - java.util.function.Supplier> functionsFactory = () -> new MaxSizeHashMap<>(fieldRunnerProperties.getFunctionsCacheSize()); + Supplier> functionsFactory = () -> new MaxSizeHashMap<>(fieldRunnerProperties.getFunctionsCacheSize()); - caches.add(new GenericMapCache<>( + caches.add(new FunctionsMapCache( CacheMapKeys.FUNCTIONS, - CachedFunction.class, - functionsFactory + functionsFactory, + fieldActionsCacheService, + petriNetService )); - java.util.function.Supplier>> nsFactory = () -> new MaxSizeHashMap<>(fieldRunnerProperties.getNamespaceCacheSize()); + Supplier>> nsFactory = () -> new MaxSizeHashMap<>(fieldRunnerProperties.getNamespaceCacheSize()); - caches.add(new GenericMapCache<>( + caches.add(new FunctionsNamespaceMapCache( CacheMapKeys.NAMESPACE_FUNCTIONS, - List.class, - nsFactory + nsFactory, + fieldActionsCacheService, + petriNetService )); SimpleCacheManager cacheManager = new SimpleCacheManager(); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java new file mode 100644 index 0000000000..70b9cdc1cb --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java @@ -0,0 +1,52 @@ +package com.netgrif.application.engine.configuration; + +import com.netgrif.application.engine.objects.petrinet.domain.Function; +import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; +import com.netgrif.application.engine.workflow.domain.CachedFunction; +import com.netgrif.application.engine.workflow.service.interfaces.IFieldActionsCacheService; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; + +@Slf4j +public class FunctionsMapCache extends GenericMapCache { + + public FunctionsMapCache(String name, java.util.function.Supplier> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService) { + super(name, CachedFunction.class, mapFactory, fieldActionsCacheService, petriNetService); + } + + @Override + public ValueWrapper get(Object key) { + String stringKey = String.valueOf(key); + + Object valueObject = map.get(stringKey); + if (valueObject != null) { + return new org.springframework.cache.support.SimpleValueWrapper(valueObject); + } + + Function function = petriNetService.findByFunctionId(stringKey); + if (function != null) { + map.put(stringKey, function); + return new org.springframework.cache.support.SimpleValueWrapper(function); + } else { + return new org.springframework.cache.support.SimpleValueWrapper(null); + } + } + + public T get(Object key, Class type) { + String stringKey = String.valueOf(key); + Object valueObject = map.get(stringKey); + + if (valueObject != null) { + return type.cast(valueObject); + } + + Function function = petriNetService.findByFunctionId(stringKey); + if (function != null) { + map.put(stringKey, function); + return type.cast(function); + } else { + return type.cast(null); + } + } +} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java new file mode 100644 index 0000000000..66850c3a7a --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java @@ -0,0 +1,45 @@ +package com.netgrif.application.engine.configuration; + +import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; +import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; +import com.netgrif.application.engine.workflow.domain.CachedFunction; +import com.netgrif.application.engine.workflow.service.interfaces.IFieldActionsCacheService; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; +import java.util.Map; + +@Slf4j +public class FunctionsNamespaceMapCache extends GenericMapCache { + + public FunctionsNamespaceMapCache(String name, java.util.function.Supplier>> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService) { + super(name, List.class, mapFactory, fieldActionsCacheService, petriNetService); + } + + @Override + public ValueWrapper get(Object key) { + String stringKey = String.valueOf(key); + + Object valueObject = map.get(stringKey); + if (valueObject != null) { + return new org.springframework.cache.support.SimpleValueWrapper(valueObject); + } + PetriNet petriNet = petriNetService.getPetriNet(stringKey); + fieldActionsCacheService.cachePetriNetFunctions(petriNet); + return new org.springframework.cache.support.SimpleValueWrapper(map.get(stringKey)); + } + + public T get(Object key, Class type) { + String stringKey = String.valueOf(key); + Object valueObject = map.get(stringKey); + + if (valueObject != null) { + return type.cast(valueObject); + } + + PetriNet petriNet = petriNetService.getPetriNet(stringKey); + fieldActionsCacheService.cachePetriNetFunctions(petriNet); + return type.cast(map.get(stringKey)); + + } +} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java index 1c53f632fb..b6e729ec69 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java @@ -1,38 +1,37 @@ package com.netgrif.application.engine.configuration; +import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; +import com.netgrif.application.engine.workflow.service.interfaces.IFieldActionsCacheService; import lombok.extern.slf4j.Slf4j; import org.springframework.cache.Cache; import java.util.Map; @Slf4j -public class GenericMapCache implements Cache { +public abstract class GenericMapCache implements Cache { private final String name; protected Class valueType; private final java.util.function.Supplier> mapFactory; - private volatile Map map; + protected volatile Map map; + protected final IFieldActionsCacheService fieldActionsCacheService; - public GenericMapCache(String name, Class valueType, java.util.function.Supplier> mapFactory) { + protected final IPetriNetService petriNetService; + + public GenericMapCache(String name, Class valueType, java.util.function.Supplier> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService) { this.name = name; this.valueType = valueType; this.mapFactory = mapFactory; this.map = mapFactory.get(); + this.fieldActionsCacheService = fieldActionsCacheService; + this.petriNetService = petriNetService; } @Override public String getName() { return name; } - @Override public Object getNativeCache() { return map; } - - @Override public ValueWrapper get(Object key) { - Object valueObject = map.get(String.valueOf(key)); - return (valueObject == null) ? null : new org.springframework.cache.support.SimpleValueWrapper(valueObject); - } - @Override public T get(Object key, Class type) { - Object valueObject = map.get(String.valueOf(key)); - return (valueObject == null) ? null : type.cast(valueObject); - } + @Override public Object getNativeCache() { return map; } - @Override public T get(Object key, java.util.concurrent.Callable loader) { + @Override + public T get(Object key, java.util.concurrent.Callable loader) { String stringKey = String.valueOf(key); Object present = map.get(stringKey); @@ -51,15 +50,18 @@ public GenericMapCache(String name, Class valueType, java.util.function.Suppl } } - @Override public void put(Object key, Object value) { + @Override + public void put(Object key, Object value) { map.put(String.valueOf(key), safeCast(value)); } - @Override public void evict(Object key) { + @Override + public void evict(Object key) { map.remove(String.valueOf(key)); } - @Override public void clear() { + @Override + public void clear() { this.map = mapFactory.get(); log.info("{} cache cleared", this.getName()); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index f9dad57a49..0aa54baa2b 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -5,6 +5,7 @@ import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import com.netgrif.application.engine.configuration.properties.CacheConfigurationProperties; import com.netgrif.application.engine.files.minio.StorageConfigurationProperties; +import com.netgrif.application.engine.objects.petrinet.domain.*; import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; import com.netgrif.application.engine.petrinet.web.responsebodies.ArcImportReference; import com.netgrif.application.engine.objects.auth.domain.Group; @@ -17,10 +18,6 @@ import com.netgrif.application.engine.objects.event.events.petrinet.ProcessDeployEvent; import com.netgrif.application.engine.importer.service.Importer; import com.netgrif.application.engine.auth.service.GroupService; -import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; -import com.netgrif.application.engine.objects.petrinet.domain.PetriNetSearch; -import com.netgrif.application.engine.objects.petrinet.domain.Transition; -import com.netgrif.application.engine.objects.petrinet.domain.VersionType; import com.netgrif.application.engine.objects.petrinet.domain.dataset.logic.action.Action; import com.netgrif.application.engine.petrinet.domain.dataset.logic.action.FieldActionsRunner; import com.netgrif.application.engine.objects.petrinet.domain.events.EventPhase; @@ -558,6 +555,19 @@ public void runActions(List actions, PetriNet petriNet) { }); } + @Override + public Function findByFunctionId(String functionId) { + Query query = new Query(); + + query.addCriteria(Criteria.where("functions._id").is(new ObjectId(functionId))); + + PetriNet petriNet = mongoTemplate.findOne(query, PetriNet.class, "petriNet"); + + Optional optionalFunction = petriNet.getFunctions().stream().filter(function -> function.getObjectId().toString().equals(functionId)).findFirst(); + + return optionalFunction.orElse(null); + } + protected T requireNonNull(T obj, Object... item) { if (obj == null) { if (item.length > 0) { @@ -567,4 +577,5 @@ protected T requireNonNull(T obj, Object... item) { } return obj; } + } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java index 73921f1e2f..d3d175fdb7 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java @@ -1,10 +1,7 @@ package com.netgrif.application.engine.petrinet.service.interfaces; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; -import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; -import com.netgrif.application.engine.objects.petrinet.domain.PetriNetSearch; -import com.netgrif.application.engine.objects.petrinet.domain.Transition; -import com.netgrif.application.engine.objects.petrinet.domain.VersionType; +import com.netgrif.application.engine.objects.petrinet.domain.*; import com.netgrif.application.engine.objects.petrinet.domain.dataset.Field; import com.netgrif.application.engine.objects.petrinet.domain.dataset.logic.action.Action; import com.netgrif.application.engine.objects.petrinet.domain.throwable.MissingIconKeyException; @@ -335,4 +332,15 @@ static DataFieldReference transformToReference(PetriNet net, Transition transiti * @return a {@link PetriNetImportReference} linking the PetriNet */ PetriNetImportReference getNetFromCase(String caseId); + + + /** + * Finds and returns a single function subdocument from the {@code petriNet} collection + * by its nested {@code functions._id}. + * + * @param functionId the string form of the function's ObjectId (24-hex) + * @return the matching {@code Function} subdocument, or {@code null} if not found + * @throws IllegalArgumentException if {@code functionId} is not a valid ObjectId + */ + Function findByFunctionId(String functionId); } \ No newline at end of file diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java index ddb8d80b30..55503ba87b 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java @@ -62,10 +62,15 @@ public void cachePetriNetFunctions(PetriNet petriNet) { } } + @Override + public void reloadCachedFunctions(String petriNetId) { + cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS).evictIfPresent(petriNetId); + cachePetriNetFunctions(petriNetService.getNewestVersionByIdentifier(petriNetId)); + } + @Override public void reloadCachedFunctions(PetriNet petriNet) { - cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS).evictIfPresent(petriNet.getIdentifier()); - cachePetriNetFunctions(petriNetService.getNewestVersionByIdentifier(petriNet.getIdentifier())); + this.reloadCachedFunctions(petriNet.getIdentifier()); } @Override @@ -75,7 +80,7 @@ public Closure getCompiledAction(Action action, boolean shouldRewriteCachedActio Object nativeActionsCache = actionsCache.getNativeCache(); if (nativeActionsCache instanceof Map map) { - if (shouldRewriteCachedActions || map.containsKey(stringId) ) { + if (shouldRewriteCachedActions || !map.containsKey(stringId) ) { Closure code = (Closure) shell.evaluate("{-> " + action.getDefinition() + "}"); actionsCache.put(stringId, code); } @@ -84,7 +89,7 @@ public Closure getCompiledAction(Action action, boolean shouldRewriteCachedActio } @Override - public List getCachedFunctions(List functions) { + public List getCachedFunctions(List functions) { List cachedFunctions = new ArrayList<>(functions.size()); Cache functionsCache = cacheManager.getCache(CacheMapKeys.FUNCTIONS); Object nativeFunctionsCache = functionsCache.getNativeCache(); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java index 6eb2580949..7303a97907 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java @@ -13,6 +13,8 @@ public interface IFieldActionsCacheService { void cachePetriNetFunctions(PetriNet petriNet); + void reloadCachedFunctions(String petriNetId); + void reloadCachedFunctions(PetriNet petriNet); Closure getCompiledAction(Action action, boolean shouldRewriteCachedActions); From e976081e79c6e32b2565cafb589c400b1aade986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Voz=C3=A1r?= Date: Thu, 4 Sep 2025 12:41:24 +0200 Subject: [PATCH 05/40] NAE-2182 - prevent multi thread unexpected behaviour and prevent cache mutation on get or put --- .../engine/configuration/ActionsMapCache.java | 15 ++--- .../configuration/CacheConfiguration.java | 19 +++--- .../configuration/FunctionsMapCache.java | 34 +++++----- .../FunctionsNamespaceMapCache.java | 20 ++++-- .../engine/configuration/GenericMapCache.java | 62 +++++++++++++------ .../workflow/domain/CachedFunction.java | 16 +++++ 6 files changed, 113 insertions(+), 53 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java index c2701c6a43..a08a03b0d7 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java @@ -4,6 +4,7 @@ import com.netgrif.application.engine.workflow.service.interfaces.IFieldActionsCacheService; import groovy.lang.Closure; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.support.SimpleValueWrapper; import java.util.Map; @@ -15,27 +16,27 @@ public ActionsMapCache(String name, java.util.function.Supplier T get(Object key, Class type) { + public synchronized T get(Object key, Class type) { String stringKey = String.valueOf(key); - Object valueObject = map.get(stringKey); + Object valueObject = map().get(stringKey); if (valueObject != null) { return type.cast(valueObject); } fieldActionsCacheService.reloadCachedFunctions(stringKey); - return type.cast(map.get(stringKey)); + return type.cast(map().get(stringKey)); } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java index db26422602..4e0245245b 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java @@ -3,6 +3,7 @@ import com.netgrif.application.engine.configuration.properties.CacheConfigurationProperties; import com.netgrif.application.engine.configuration.properties.RunnerConfigurationProperties; import com.netgrif.application.engine.elastic.service.executors.MaxSizeHashMap; +import com.netgrif.application.engine.event.IGroovyShellFactory; import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; import com.netgrif.application.engine.workflow.domain.CachedFunction; import com.netgrif.application.engine.workflow.service.interfaces.IFieldActionsCacheService; @@ -28,15 +29,15 @@ public class CacheConfiguration extends CachingConfigurerSupport { private final RunnerConfigurationProperties.FieldRunnerProperties fieldRunnerProperties; private final CacheConfigurationProperties properties; private final IFieldActionsCacheService fieldActionsCacheService; - private final IPetriNetService petriNetService; + private final IGroovyShellFactory groovyShellFactory; - - public CacheConfiguration(RunnerConfigurationProperties.FieldRunnerProperties fieldRunnerProperties, CacheConfigurationProperties properties, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService) { + public CacheConfiguration(RunnerConfigurationProperties.FieldRunnerProperties fieldRunnerProperties, CacheConfigurationProperties properties, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService, IGroovyShellFactory shellFactory) { this.fieldRunnerProperties = fieldRunnerProperties; this.properties = properties; this.fieldActionsCacheService = fieldActionsCacheService; this.petriNetService = petriNetService; + this.groovyShellFactory = shellFactory; } @Bean @@ -49,7 +50,8 @@ public CacheManager cacheManager() { .collect(Collectors.toCollection(ArrayList::new)); - Supplier> actionsFactory = () -> new MaxSizeHashMap<>(fieldRunnerProperties.getActionCacheSize()); + Supplier> actionsFactory = + () -> Collections.synchronizedMap(new MaxSizeHashMap<>(fieldRunnerProperties.getActionCacheSize())); caches.add(new ActionsMapCache( CacheMapKeys.ACTIONS, @@ -58,16 +60,19 @@ public CacheManager cacheManager() { petriNetService )); - Supplier> functionsFactory = () -> new MaxSizeHashMap<>(fieldRunnerProperties.getFunctionsCacheSize()); + Supplier> functionsFactory = + () -> Collections.synchronizedMap(new MaxSizeHashMap<>(fieldRunnerProperties.getFunctionsCacheSize())); caches.add(new FunctionsMapCache( CacheMapKeys.FUNCTIONS, functionsFactory, fieldActionsCacheService, - petriNetService + petriNetService, + groovyShellFactory )); - Supplier>> nsFactory = () -> new MaxSizeHashMap<>(fieldRunnerProperties.getNamespaceCacheSize()); + Supplier>> nsFactory = + () -> Collections.synchronizedMap(new MaxSizeHashMap<>(fieldRunnerProperties.getNamespaceCacheSize())); caches.add(new FunctionsNamespaceMapCache( CacheMapKeys.NAMESPACE_FUNCTIONS, diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java index 70b9cdc1cb..38b0576079 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java @@ -1,41 +1,47 @@ package com.netgrif.application.engine.configuration; +import com.netgrif.application.engine.event.IGroovyShellFactory; import com.netgrif.application.engine.objects.petrinet.domain.Function; import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; import com.netgrif.application.engine.workflow.domain.CachedFunction; import com.netgrif.application.engine.workflow.service.interfaces.IFieldActionsCacheService; +import groovy.lang.GroovyShell; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.support.SimpleValueWrapper; import java.util.Map; @Slf4j public class FunctionsMapCache extends GenericMapCache { - public FunctionsMapCache(String name, java.util.function.Supplier> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService) { + private final GroovyShell shell; + + public FunctionsMapCache(String name, java.util.function.Supplier> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService, IGroovyShellFactory shellFactory) { super(name, CachedFunction.class, mapFactory, fieldActionsCacheService, petriNetService); + this.shell = shellFactory.getGroovyShell(); } @Override - public ValueWrapper get(Object key) { + public synchronized ValueWrapper get(Object key) { String stringKey = String.valueOf(key); - Object valueObject = map.get(stringKey); + Object valueObject = map().get(stringKey); if (valueObject != null) { - return new org.springframework.cache.support.SimpleValueWrapper(valueObject); + return new SimpleValueWrapper(CachedFunction.copyOf(shell, (CachedFunction) valueObject)); } Function function = petriNetService.findByFunctionId(stringKey); if (function != null) { - map.put(stringKey, function); - return new org.springframework.cache.support.SimpleValueWrapper(function); - } else { - return new org.springframework.cache.support.SimpleValueWrapper(null); + map().put(stringKey, CachedFunction.build(shell, function)); + return new SimpleValueWrapper(CachedFunction.build(shell, function)); } + + return new SimpleValueWrapper(null); } - public T get(Object key, Class type) { + public synchronized T get(Object key, Class type) { String stringKey = String.valueOf(key); - Object valueObject = map.get(stringKey); + Object valueObject = map().get(stringKey); if (valueObject != null) { return type.cast(valueObject); @@ -43,10 +49,10 @@ public T get(Object key, Class type) { Function function = petriNetService.findByFunctionId(stringKey); if (function != null) { - map.put(stringKey, function); - return type.cast(function); - } else { - return type.cast(null); + map().put(stringKey, CachedFunction.build(shell, function)); + return type.cast(CachedFunction.build(shell, function)); } + + return type.cast(null); } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java index 66850c3a7a..467b80eb3a 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java @@ -1,10 +1,12 @@ package com.netgrif.application.engine.configuration; +import com.netgrif.application.engine.objects.petrinet.domain.Function; import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; import com.netgrif.application.engine.workflow.domain.CachedFunction; import com.netgrif.application.engine.workflow.service.interfaces.IFieldActionsCacheService; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.support.SimpleValueWrapper; import java.util.List; import java.util.Map; @@ -20,26 +22,32 @@ public FunctionsNamespaceMapCache(String name, java.util.function.Supplier) valueObject)); } PetriNet petriNet = petriNetService.getPetriNet(stringKey); fieldActionsCacheService.cachePetriNetFunctions(petriNet); - return new org.springframework.cache.support.SimpleValueWrapper(map.get(stringKey)); + return new SimpleValueWrapper(List.copyOf((List) map().get(stringKey))); } public T get(Object key, Class type) { String stringKey = String.valueOf(key); - Object valueObject = map.get(stringKey); + Object valueObject = map().get(stringKey); if (valueObject != null) { - return type.cast(valueObject); + return type.cast(List.copyOf((List) valueObject)); } PetriNet petriNet = petriNetService.getPetriNet(stringKey); fieldActionsCacheService.cachePetriNetFunctions(petriNet); - return type.cast(map.get(stringKey)); + return type.cast(List.copyOf((List) map().get(stringKey))); } + + @Override + public void put(Object key, Object value) { + String k = String.valueOf(key); + map().put(k, List.copyOf((List) value)); + } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java index b6e729ec69..6a869653c5 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java @@ -4,15 +4,21 @@ import com.netgrif.application.engine.workflow.service.interfaces.IFieldActionsCacheService; import lombok.extern.slf4j.Slf4j; import org.springframework.cache.Cache; +import org.springframework.security.core.parameters.P; import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; @Slf4j public abstract class GenericMapCache implements Cache { private final String name; protected Class valueType; private final java.util.function.Supplier> mapFactory; - protected volatile Map map; + private final AtomicReference> atomicMapRef; + + private final ConcurrentHashMap locks = new ConcurrentHashMap<>(); protected final IFieldActionsCacheService fieldActionsCacheService; protected final IPetriNetService petriNetService; @@ -21,51 +27,69 @@ public GenericMapCache(String name, Class valueType, java.util.function.Suppl this.name = name; this.valueType = valueType; this.mapFactory = mapFactory; - this.map = mapFactory.get(); + this.atomicMapRef = new AtomicReference<>(mapFactory.get()); this.fieldActionsCacheService = fieldActionsCacheService; this.petriNetService = petriNetService; } @Override public String getName() { return name; } - @Override public Object getNativeCache() { return map; } + @Override public Object getNativeCache() { return Map.copyOf(map()); } @Override - public T get(Object key, java.util.concurrent.Callable loader) { - String stringKey = String.valueOf(key); - Object present = map.get(stringKey); + @SuppressWarnings("unchecked") + public T get(Object key, Callable loader) { + final String stringKey = String.valueOf(key); - if (present != null) { - return (T) present; + Object mapValue = map().get(stringKey); + if (mapValue != null) { + return (T) mapValue; } + Object lock = locks.computeIfAbsent(stringKey, lockKey -> new Object()); try { - T computed = loader.call(); - if (computed != null) { - map.put(stringKey, safeCast(computed)); + synchronized (lock) { + Object mapLockValue = map().get(stringKey); + if (mapLockValue != null) { + return (T) mapLockValue; + } + + T computed = loader.call(); + if (computed == null) { + return null; + } + + V value = safeCast(computed); + map().put(stringKey, value); + return (T) value; } - return computed; } catch (Exception ex) { - throw new org.springframework.cache.Cache.ValueRetrievalException(stringKey, loader, ex); + throw new ValueRetrievalException(stringKey, loader, ex); + } finally { + locks.remove(stringKey, lock); } } @Override - public void put(Object key, Object value) { - map.put(String.valueOf(key), safeCast(value)); + public synchronized void put(Object key, Object value) { + map().put(String.valueOf(key), safeCast(value)); } @Override - public void evict(Object key) { - map.remove(String.valueOf(key)); + public synchronized void evict(Object key) { + map().remove(String.valueOf(key)); } @Override - public void clear() { - this.map = mapFactory.get(); + public synchronized void clear() { + this.atomicMapRef.set(mapFactory.get()); log.info("{} cache cleared", this.getName()); } + protected Map map() { + return atomicMapRef.get(); + } + protected V safeCast(Object object) { if (object == null) { return null; diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/domain/CachedFunction.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/domain/CachedFunction.java index 55a6b3f890..139b7c8933 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/domain/CachedFunction.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/domain/CachedFunction.java @@ -23,4 +23,20 @@ public static CachedFunction build(GroovyShell shell, Function function) { .function(function) .build(); } + + public static CachedFunction copyOf(GroovyShell shell, CachedFunction cachedFunction) { + if (cachedFunction == null) { + return null; + } + + Closure code = (Closure) shell.evaluate(cachedFunction.getFunction().getDefinition()); + if (code == null) { + throw new IllegalArgumentException("Non compilable function"); + } + + return CachedFunction.builder() + .code(code) + .function(new Function(cachedFunction.getFunction())) + .build(); + } } From 8323adb34776bdd661269f18957ff6bff31e1d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Voz=C3=A1r?= Date: Tue, 16 Sep 2025 11:00:50 +0200 Subject: [PATCH 06/40] NAE-2182 - Implementation of generic map cache and changed store for namespaceFunctionsCache from local Map to Redis --- .../engine/configuration/ActionsMapCache.java | 42 -------------- .../configuration/CacheConfiguration.java | 36 +++--------- .../engine/configuration/CacheMapKeys.java | 5 +- .../configuration/FunctionsMapCache.java | 58 ------------------- .../FunctionsNamespaceMapCache.java | 53 ----------------- .../engine/configuration/GenericMapCache.java | 38 ++++++++---- .../CacheConfigurationProperties.java | 7 ++- .../service/FieldActionsCacheService.java | 13 +++-- 8 files changed, 51 insertions(+), 201 deletions(-) delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java deleted file mode 100644 index a08a03b0d7..0000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.netgrif.application.engine.configuration; - -import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; -import com.netgrif.application.engine.workflow.service.interfaces.IFieldActionsCacheService; -import groovy.lang.Closure; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.support.SimpleValueWrapper; - -import java.util.Map; - -@Slf4j -public class ActionsMapCache extends GenericMapCache { - - public ActionsMapCache(String name, java.util.function.Supplier> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService) { - super(name, Closure.class, mapFactory, fieldActionsCacheService, petriNetService); - } - - @Override - public synchronized ValueWrapper get(Object key) { - String stringKey = String.valueOf(key); - - Object valueObject = map().get(stringKey); - if (valueObject != null) { - return new SimpleValueWrapper(valueObject); - } - fieldActionsCacheService.reloadCachedFunctions(stringKey); - return new SimpleValueWrapper(map().get(stringKey)); - } - - public synchronized T get(Object key, Class type) { - String stringKey = String.valueOf(key); - Object valueObject = map().get(stringKey); - - if (valueObject != null) { - return type.cast(valueObject); - } - - fieldActionsCacheService.reloadCachedFunctions(stringKey); - return type.cast(map().get(stringKey)); - - } -} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java index 4e0245245b..4d8849fcfc 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java @@ -3,10 +3,7 @@ import com.netgrif.application.engine.configuration.properties.CacheConfigurationProperties; import com.netgrif.application.engine.configuration.properties.RunnerConfigurationProperties; import com.netgrif.application.engine.elastic.service.executors.MaxSizeHashMap; -import com.netgrif.application.engine.event.IGroovyShellFactory; -import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; import com.netgrif.application.engine.workflow.domain.CachedFunction; -import com.netgrif.application.engine.workflow.service.interfaces.IFieldActionsCacheService; import groovy.lang.Closure; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; @@ -28,16 +25,10 @@ public class CacheConfiguration extends CachingConfigurerSupport { private final RunnerConfigurationProperties.FieldRunnerProperties fieldRunnerProperties; private final CacheConfigurationProperties properties; - private final IFieldActionsCacheService fieldActionsCacheService; - private final IPetriNetService petriNetService; - private final IGroovyShellFactory groovyShellFactory; - public CacheConfiguration(RunnerConfigurationProperties.FieldRunnerProperties fieldRunnerProperties, CacheConfigurationProperties properties, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService, IGroovyShellFactory shellFactory) { + public CacheConfiguration(RunnerConfigurationProperties.FieldRunnerProperties fieldRunnerProperties, CacheConfigurationProperties properties) { this.fieldRunnerProperties = fieldRunnerProperties; this.properties = properties; - this.fieldActionsCacheService = fieldActionsCacheService; - this.petriNetService = petriNetService; - this.groovyShellFactory = shellFactory; } @Bean @@ -53,32 +44,19 @@ public CacheManager cacheManager() { Supplier> actionsFactory = () -> Collections.synchronizedMap(new MaxSizeHashMap<>(fieldRunnerProperties.getActionCacheSize())); - caches.add(new ActionsMapCache( + caches.add(new GenericMapCache<>( CacheMapKeys.ACTIONS, - actionsFactory, - fieldActionsCacheService, - petriNetService + Closure.class, + actionsFactory )); Supplier> functionsFactory = () -> Collections.synchronizedMap(new MaxSizeHashMap<>(fieldRunnerProperties.getFunctionsCacheSize())); - caches.add(new FunctionsMapCache( + caches.add(new GenericMapCache<>( CacheMapKeys.FUNCTIONS, - functionsFactory, - fieldActionsCacheService, - petriNetService, - groovyShellFactory - )); - - Supplier>> nsFactory = - () -> Collections.synchronizedMap(new MaxSizeHashMap<>(fieldRunnerProperties.getNamespaceCacheSize())); - - caches.add(new FunctionsNamespaceMapCache( - CacheMapKeys.NAMESPACE_FUNCTIONS, - nsFactory, - fieldActionsCacheService, - petriNetService + CachedFunction.class, + functionsFactory )); SimpleCacheManager cacheManager = new SimpleCacheManager(); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java index 8928b0f43b..c99f7a3985 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java @@ -1,7 +1,6 @@ package com.netgrif.application.engine.configuration; public class CacheMapKeys { - public static final String ACTIONS = "actionsCache"; - public static final String FUNCTIONS = "functionsCache"; - public static final String NAMESPACE_FUNCTIONS = "namespaceFunctionsCache"; + public static final String ACTIONS = "actions"; + public static final String FUNCTIONS = "functions"; } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java deleted file mode 100644 index 38b0576079..0000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.netgrif.application.engine.configuration; - -import com.netgrif.application.engine.event.IGroovyShellFactory; -import com.netgrif.application.engine.objects.petrinet.domain.Function; -import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; -import com.netgrif.application.engine.workflow.domain.CachedFunction; -import com.netgrif.application.engine.workflow.service.interfaces.IFieldActionsCacheService; -import groovy.lang.GroovyShell; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.support.SimpleValueWrapper; - -import java.util.Map; - -@Slf4j -public class FunctionsMapCache extends GenericMapCache { - - private final GroovyShell shell; - - public FunctionsMapCache(String name, java.util.function.Supplier> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService, IGroovyShellFactory shellFactory) { - super(name, CachedFunction.class, mapFactory, fieldActionsCacheService, petriNetService); - this.shell = shellFactory.getGroovyShell(); - } - - @Override - public synchronized ValueWrapper get(Object key) { - String stringKey = String.valueOf(key); - - Object valueObject = map().get(stringKey); - if (valueObject != null) { - return new SimpleValueWrapper(CachedFunction.copyOf(shell, (CachedFunction) valueObject)); - } - - Function function = petriNetService.findByFunctionId(stringKey); - if (function != null) { - map().put(stringKey, CachedFunction.build(shell, function)); - return new SimpleValueWrapper(CachedFunction.build(shell, function)); - } - - return new SimpleValueWrapper(null); - } - - public synchronized T get(Object key, Class type) { - String stringKey = String.valueOf(key); - Object valueObject = map().get(stringKey); - - if (valueObject != null) { - return type.cast(valueObject); - } - - Function function = petriNetService.findByFunctionId(stringKey); - if (function != null) { - map().put(stringKey, CachedFunction.build(shell, function)); - return type.cast(CachedFunction.build(shell, function)); - } - - return type.cast(null); - } -} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java deleted file mode 100644 index 467b80eb3a..0000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.netgrif.application.engine.configuration; - -import com.netgrif.application.engine.objects.petrinet.domain.Function; -import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; -import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; -import com.netgrif.application.engine.workflow.domain.CachedFunction; -import com.netgrif.application.engine.workflow.service.interfaces.IFieldActionsCacheService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.support.SimpleValueWrapper; - -import java.util.List; -import java.util.Map; - -@Slf4j -public class FunctionsNamespaceMapCache extends GenericMapCache { - - public FunctionsNamespaceMapCache(String name, java.util.function.Supplier>> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService) { - super(name, List.class, mapFactory, fieldActionsCacheService, petriNetService); - } - - @Override - public ValueWrapper get(Object key) { - String stringKey = String.valueOf(key); - - Object valueObject = map().get(stringKey); - if (valueObject != null) { - return new SimpleValueWrapper(List.copyOf((List) valueObject)); - } - PetriNet petriNet = petriNetService.getPetriNet(stringKey); - fieldActionsCacheService.cachePetriNetFunctions(petriNet); - return new SimpleValueWrapper(List.copyOf((List) map().get(stringKey))); - } - - public T get(Object key, Class type) { - String stringKey = String.valueOf(key); - Object valueObject = map().get(stringKey); - - if (valueObject != null) { - return type.cast(List.copyOf((List) valueObject)); - } - - PetriNet petriNet = petriNetService.getPetriNet(stringKey); - fieldActionsCacheService.cachePetriNetFunctions(petriNet); - return type.cast(List.copyOf((List) map().get(stringKey))); - - } - - @Override - public void put(Object key, Object value) { - String k = String.valueOf(key); - map().put(k, List.copyOf((List) value)); - } -} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java index 6a869653c5..44ac966014 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java @@ -1,10 +1,8 @@ package com.netgrif.application.engine.configuration; -import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; -import com.netgrif.application.engine.workflow.service.interfaces.IFieldActionsCacheService; import lombok.extern.slf4j.Slf4j; import org.springframework.cache.Cache; -import org.springframework.security.core.parameters.P; +import org.springframework.cache.support.SimpleValueWrapper; import java.util.Map; import java.util.concurrent.Callable; @@ -12,24 +10,19 @@ import java.util.concurrent.atomic.AtomicReference; @Slf4j -public abstract class GenericMapCache implements Cache { +public class GenericMapCache implements Cache { private final String name; protected Class valueType; private final java.util.function.Supplier> mapFactory; private final AtomicReference> atomicMapRef; private final ConcurrentHashMap locks = new ConcurrentHashMap<>(); - protected final IFieldActionsCacheService fieldActionsCacheService; - protected final IPetriNetService petriNetService; - - public GenericMapCache(String name, Class valueType, java.util.function.Supplier> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService) { + public GenericMapCache(String name, Class valueType, java.util.function.Supplier> mapFactory) { this.name = name; this.valueType = valueType; this.mapFactory = mapFactory; this.atomicMapRef = new AtomicReference<>(mapFactory.get()); - this.fieldActionsCacheService = fieldActionsCacheService; - this.petriNetService = petriNetService; } @Override public String getName() { return name; } @@ -70,6 +63,31 @@ public T get(Object key, Callable loader) { } } + @Override + public synchronized ValueWrapper get(Object key) { + String stringKey = String.valueOf(key); + + Object valueObject = map().get(stringKey); + if (valueObject != null) { + return new SimpleValueWrapper(valueObject); + } + + return new SimpleValueWrapper(null); + } + + @Override + public synchronized T get(Object key, Class type) { + String stringKey = String.valueOf(key); + Object valueObject = map().get(stringKey); + + if (valueObject != null) { + return type.cast(valueObject); + } + + return type.cast(null); + + } + @Override public synchronized void put(Object key, Object value) { map().put(String.valueOf(key), safeCast(value)); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java index 8050c7ac37..755829b770 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java @@ -41,6 +41,11 @@ public class CacheConfigurationProperties { */ private String loadedModules = "loadedModules"; + /** + * Default cache name for caching namespace functions. + */ + private String namespaceFunctions = "namespaceFunctions"; + /** * A list of additional custom cache names. * Allows users to define their own cache names for specific use cases. @@ -54,7 +59,7 @@ public class CacheConfigurationProperties { * @return a {@link Set} of all cache names. */ public Set getAllCaches() { - Set caches = new LinkedHashSet<>(Arrays.asList(petriNetById, petriNetByIdentifier, petriNetNewest, petriNetCache, loadedModules)); + Set caches = new LinkedHashSet<>(Arrays.asList(petriNetById, petriNetByIdentifier, petriNetNewest, petriNetCache, loadedModules, namespaceFunctions)); caches.addAll(additional); return caches; } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java index 55503ba87b..b0f7317bb5 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java @@ -1,6 +1,7 @@ package com.netgrif.application.engine.workflow.service; import com.netgrif.application.engine.configuration.CacheMapKeys; +import com.netgrif.application.engine.configuration.properties.CacheConfigurationProperties; import com.netgrif.application.engine.event.IGroovyShellFactory; import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; import com.netgrif.application.engine.objects.petrinet.domain.Function; @@ -25,13 +26,15 @@ @Slf4j public class FieldActionsCacheService implements IFieldActionsCacheService { + private final CacheConfigurationProperties properties; private final CacheManager cacheManager; private IPetriNetService petriNetService; private final GroovyShell shell; - public FieldActionsCacheService(CacheManager cacheManager, IGroovyShellFactory shellFactory) { + public FieldActionsCacheService(CacheConfigurationProperties properties, CacheManager cacheManager, IGroovyShellFactory shellFactory) { + this.properties = properties; this.cacheManager = cacheManager; this.shell = shellFactory.getGroovyShell(); } @@ -52,7 +55,7 @@ public void cachePetriNetFunctions(PetriNet petriNet) { .map(function -> CachedFunction.build(shell, function)) .collect(Collectors.toList()); - Cache namespaceFunctionsCache = cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS); + Cache namespaceFunctionsCache = cacheManager.getCache(properties.getNamespaceFunctions()); if (!functions.isEmpty()) { evaluateCachedFunctions(functions); @@ -64,7 +67,7 @@ public void cachePetriNetFunctions(PetriNet petriNet) { @Override public void reloadCachedFunctions(String petriNetId) { - cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS).evictIfPresent(petriNetId); + cacheManager.getCache(properties.getNamespaceFunctions()).evictIfPresent(petriNetId); cachePetriNetFunctions(petriNetService.getNewestVersionByIdentifier(petriNetId)); } @@ -144,7 +147,7 @@ private String stringifyParameterTypes(Class[] a) { @Override public Map> getNamespaceFunctionCache() { - return new HashMap<>((Map) cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS).getNativeCache()); + return new HashMap<>((Map) cacheManager.getCache(properties.getNamespaceFunctions()).getNativeCache()); } @Override @@ -154,7 +157,7 @@ public void clearActionCache() { @Override public void clearNamespaceFunctionCache() { - cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS).clear(); + cacheManager.getCache(properties.getNamespaceFunctions()).clear(); } @Override From f9796d66606a50b6d457df16f3769ef354961a32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Voz=C3=A1r?= Date: Tue, 16 Sep 2025 11:22:37 +0200 Subject: [PATCH 07/40] NAE-2182 - removal of unused methods, correction of several issues due to changes in cache structure --- .../engine/configuration/CacheMapKeys.java | 3 ++- .../engine/configuration/GenericMapCache.java | 20 +++++++------------ .../petrinet/service/PetriNetService.java | 13 ------------ .../service/interfaces/IPetriNetService.java | 11 ---------- .../workflow/domain/CachedFunction.java | 16 --------------- 5 files changed, 9 insertions(+), 54 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java index c99f7a3985..f5db1e619e 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java @@ -1,6 +1,7 @@ package com.netgrif.application.engine.configuration; -public class CacheMapKeys { +public final class CacheMapKeys { + private CacheMapKeys() {} public static final String ACTIONS = "actions"; public static final String FUNCTIONS = "functions"; } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java index 44ac966014..a31ef3e511 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java @@ -12,13 +12,13 @@ @Slf4j public class GenericMapCache implements Cache { private final String name; - protected Class valueType; + private final Class valueType; private final java.util.function.Supplier> mapFactory; private final AtomicReference> atomicMapRef; private final ConcurrentHashMap locks = new ConcurrentHashMap<>(); - public GenericMapCache(String name, Class valueType, java.util.function.Supplier> mapFactory) { + public GenericMapCache(String name, Class valueType, java.util.function.Supplier> mapFactory) { this.name = name; this.valueType = valueType; this.mapFactory = mapFactory; @@ -30,7 +30,6 @@ public GenericMapCache(String name, Class valueType, java.util.function.Suppl @Override public Object getNativeCache() { return Map.copyOf(map()); } @Override - @SuppressWarnings("unchecked") public T get(Object key, Callable loader) { final String stringKey = String.valueOf(key); @@ -57,7 +56,7 @@ public T get(Object key, Callable loader) { return (T) value; } } catch (Exception ex) { - throw new ValueRetrievalException(stringKey, loader, ex); + throw new Cache.ValueRetrievalException(stringKey, loader, ex); } finally { locks.remove(stringKey, lock); } @@ -66,13 +65,8 @@ public T get(Object key, Callable loader) { @Override public synchronized ValueWrapper get(Object key) { String stringKey = String.valueOf(key); - Object valueObject = map().get(stringKey); - if (valueObject != null) { - return new SimpleValueWrapper(valueObject); - } - - return new SimpleValueWrapper(null); + return valueObject != null ? new SimpleValueWrapper(valueObject) : null; } @Override @@ -101,14 +95,14 @@ public synchronized void evict(Object key) { @Override public synchronized void clear() { this.atomicMapRef.set(mapFactory.get()); - log.info("{} cache cleared", this.getName()); } - protected Map map() { + private Map map() { return atomicMapRef.get(); } - protected V safeCast(Object object) { + @SuppressWarnings("unchecked") + private V safeCast(Object object) { if (object == null) { return null; } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index 0aa54baa2b..bb63fc3f4e 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -555,19 +555,6 @@ public void runActions(List actions, PetriNet petriNet) { }); } - @Override - public Function findByFunctionId(String functionId) { - Query query = new Query(); - - query.addCriteria(Criteria.where("functions._id").is(new ObjectId(functionId))); - - PetriNet petriNet = mongoTemplate.findOne(query, PetriNet.class, "petriNet"); - - Optional optionalFunction = petriNet.getFunctions().stream().filter(function -> function.getObjectId().toString().equals(functionId)).findFirst(); - - return optionalFunction.orElse(null); - } - protected T requireNonNull(T obj, Object... item) { if (obj == null) { if (item.length > 0) { diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java index d3d175fdb7..5b0a63a853 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java @@ -332,15 +332,4 @@ static DataFieldReference transformToReference(PetriNet net, Transition transiti * @return a {@link PetriNetImportReference} linking the PetriNet */ PetriNetImportReference getNetFromCase(String caseId); - - - /** - * Finds and returns a single function subdocument from the {@code petriNet} collection - * by its nested {@code functions._id}. - * - * @param functionId the string form of the function's ObjectId (24-hex) - * @return the matching {@code Function} subdocument, or {@code null} if not found - * @throws IllegalArgumentException if {@code functionId} is not a valid ObjectId - */ - Function findByFunctionId(String functionId); } \ No newline at end of file diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/domain/CachedFunction.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/domain/CachedFunction.java index 139b7c8933..55a6b3f890 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/domain/CachedFunction.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/domain/CachedFunction.java @@ -23,20 +23,4 @@ public static CachedFunction build(GroovyShell shell, Function function) { .function(function) .build(); } - - public static CachedFunction copyOf(GroovyShell shell, CachedFunction cachedFunction) { - if (cachedFunction == null) { - return null; - } - - Closure code = (Closure) shell.evaluate(cachedFunction.getFunction().getDefinition()); - if (code == null) { - throw new IllegalArgumentException("Non compilable function"); - } - - return CachedFunction.builder() - .code(code) - .function(new Function(cachedFunction.getFunction())) - .build(); - } } From a00263672f26f4bf388f7db461d9ed625aae1cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Voz=C3=A1r?= Date: Wed, 24 Sep 2025 13:00:48 +0200 Subject: [PATCH 08/40] NAE-2212 - added method that will cache all petriNets functions --- .../service/FieldActionsCacheService.java | 23 +++++++++++++++++-- .../interfaces/IFieldActionsCacheService.java | 2 ++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java index 1b996bc60d..5f158efb7c 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java @@ -15,6 +15,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Query; import org.springframework.stereotype.Service; import java.util.ArrayList; @@ -22,6 +24,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import java.util.stream.Stream; @Service @Slf4j @@ -30,14 +33,15 @@ public class FieldActionsCacheService implements IFieldActionsCacheService { private final RunnerConfigurationProperties.FieldRunnerProperties properties; private IPetriNetService petriNetService; - + private MongoTemplate mongoTemplate; private Map actionsCache; private Map> namespaceFunctionsCache; private Map functionsCache; private final GroovyShell shell; - public FieldActionsCacheService(RunnerConfigurationProperties.FieldRunnerProperties properties, IGroovyShellFactory shellFactory) { + public FieldActionsCacheService(RunnerConfigurationProperties.FieldRunnerProperties properties, IGroovyShellFactory shellFactory, MongoTemplate mongoTemplate) { this.properties = properties; + this.mongoTemplate = mongoTemplate; this.actionsCache = new MaxSizeHashMap<>(properties.getActionCacheSize()); this.functionsCache = new MaxSizeHashMap<>(properties.getFunctionsCacheSize()); this.namespaceFunctionsCache = new MaxSizeHashMap<>(properties.getNamespaceCacheSize()); @@ -96,6 +100,21 @@ public List getCachedFunctions(List stream = mongoTemplate.query(PetriNet.class).matching(query).stream()) { + stream.forEach(petriNet -> { + if (petriNet == null) return; + try { + this.cachePetriNetFunctions(petriNet); + } catch (Exception e) { + log.warn("Failed to cache functions for PetriNet id={}", petriNet.getStringId(), e); + } + }); + } + } + @Override public void evaluateFunctions(List functions) { evaluateCachedFunctions(functions.stream().map(function -> CachedFunction.build(shell, function)).collect(Collectors.toList())); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java index 6eb2580949..10eb67bc21 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java @@ -27,5 +27,7 @@ public interface IFieldActionsCacheService { void clearNamespaceFunctionCache(); + void cacheAllPetriNetsFunctions(); + void clearFunctionCache(); } From e6e8dacdba5976636c17f8998098b38fa06b7bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Voz=C3=A1r?= Date: Thu, 25 Sep 2025 21:06:05 +0200 Subject: [PATCH 09/40] NAE-2212 - added prevention of cache namespaceFunctions deletion from worker nodes --- .../configuration/CacheConfiguration.java | 4 +- .../configuration/cache/CacheMapKeys.java | 5 +++ .../configuration/cache/ReadOnlyCache.java | 42 +++++++++++++++++++ .../cache/WorkerConcurrentCacheManager.java | 23 ++++++++++ .../CacheConfigurationProperties.java | 7 +++- 5 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/CacheMapKeys.java create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/ReadOnlyCache.java create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java index 1e0ad25c87..685aab2a96 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java @@ -1,10 +1,10 @@ package com.netgrif.application.engine.configuration; +import com.netgrif.application.engine.configuration.cache.WorkerConcurrentCacheManager; import com.netgrif.application.engine.configuration.properties.CacheConfigurationProperties; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; -import org.springframework.cache.concurrent.ConcurrentMapCacheManager; import org.springframework.cache.interceptor.CacheResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -24,7 +24,7 @@ public CacheConfiguration(CacheConfigurationProperties properties) { @Primary @Override public CacheManager cacheManager() { - return new ConcurrentMapCacheManager(properties.getAllCaches().toArray(String[]::new)); + return new WorkerConcurrentCacheManager(properties.getAllCaches().toArray(String[]::new)); } @Bean diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/CacheMapKeys.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/CacheMapKeys.java new file mode 100644 index 0000000000..e6c776944d --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/CacheMapKeys.java @@ -0,0 +1,5 @@ +package com.netgrif.application.engine.configuration.cache; + +public class CacheMapKeys { + public static final String NAMESPACE_FUNCTIONS = "namespaceFunctionsCache"; +} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/ReadOnlyCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/ReadOnlyCache.java new file mode 100644 index 0000000000..10fae378d6 --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/ReadOnlyCache.java @@ -0,0 +1,42 @@ +package com.netgrif.application.engine.configuration.cache; + +import org.springframework.cache.Cache; + +import java.util.concurrent.Callable; + +public class ReadOnlyCache implements Cache { + + private final Cache cacheDelegate; + + public ReadOnlyCache(Cache cacheDelegate) { + this.cacheDelegate = cacheDelegate; + } + + @Override + public String getName() { return cacheDelegate.getName(); } + + @Override + public Object getNativeCache() { return cacheDelegate.getNativeCache(); } + + @Override + public ValueWrapper get(Object key) { return cacheDelegate.get(key); } + + @Override + public T get(Object key, Class type) { return cacheDelegate.get(key, type); } + + @Override + public T get(Object key, Callable loader) { return cacheDelegate.get(key, loader); } + + @Override + public void put(Object key, Object value) { cacheDelegate.put(key, value); } + + @Override + public void evict(Object key) { + throw new UnsupportedOperationException("Evict not allowed on worker for " + getName()); + } + + @Override + public void clear() { + throw new UnsupportedOperationException("Clear not allowed on worker for " + getName()); + } +} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java new file mode 100644 index 0000000000..6b8bd5fc0c --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java @@ -0,0 +1,23 @@ +package com.netgrif.application.engine.configuration.cache; + +import org.springframework.cache.Cache; +import org.springframework.cache.concurrent.ConcurrentMapCacheManager; + +public class WorkerConcurrentCacheManager extends ConcurrentMapCacheManager { + public WorkerConcurrentCacheManager() { + super(); + } + + public WorkerConcurrentCacheManager(String... cacheNames) { + super(cacheNames); + } + + @Override + protected Cache createConcurrentMapCache(String name) { + Cache base = super.createConcurrentMapCache(name); + if (CacheMapKeys.NAMESPACE_FUNCTIONS.equals(name)) { + return new ReadOnlyCache(base); + } + return base; + } +} \ No newline at end of file diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java index 8050c7ac37..755829b770 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java @@ -41,6 +41,11 @@ public class CacheConfigurationProperties { */ private String loadedModules = "loadedModules"; + /** + * Default cache name for caching namespace functions. + */ + private String namespaceFunctions = "namespaceFunctions"; + /** * A list of additional custom cache names. * Allows users to define their own cache names for specific use cases. @@ -54,7 +59,7 @@ public class CacheConfigurationProperties { * @return a {@link Set} of all cache names. */ public Set getAllCaches() { - Set caches = new LinkedHashSet<>(Arrays.asList(petriNetById, petriNetByIdentifier, petriNetNewest, petriNetCache, loadedModules)); + Set caches = new LinkedHashSet<>(Arrays.asList(petriNetById, petriNetByIdentifier, petriNetNewest, petriNetCache, loadedModules, namespaceFunctions)); caches.addAll(additional); return caches; } From d2227d0975a89a6816c9aa899e8f9b47d9588686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Voz=C3=A1r?= Date: Thu, 25 Sep 2025 21:41:08 +0200 Subject: [PATCH 10/40] NAE-2182 - optimized imports, added helper and refactor code --- .../configuration/CacheConfiguration.java | 7 ++---- .../engine/configuration/GenericMapCache.java | 10 ++------ .../petrinet/service/PetriNetService.java | 6 +++-- .../service/FieldActionsCacheService.java | 24 ++++++++++++------- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java index 4d8849fcfc..7a54194073 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java @@ -5,6 +5,7 @@ import com.netgrif.application.engine.elastic.service.executors.MaxSizeHashMap; import com.netgrif.application.engine.workflow.domain.CachedFunction; import groovy.lang.Closure; +import lombok.RequiredArgsConstructor; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; @@ -22,15 +23,11 @@ @Configuration @EnableCaching +@RequiredArgsConstructor public class CacheConfiguration extends CachingConfigurerSupport { private final RunnerConfigurationProperties.FieldRunnerProperties fieldRunnerProperties; private final CacheConfigurationProperties properties; - public CacheConfiguration(RunnerConfigurationProperties.FieldRunnerProperties fieldRunnerProperties, CacheConfigurationProperties properties) { - this.fieldRunnerProperties = fieldRunnerProperties; - this.properties = properties; - } - @Bean @Primary @Override diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java index a31ef3e511..d2a12aecc1 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java @@ -63,7 +63,7 @@ public T get(Object key, Callable loader) { } @Override - public synchronized ValueWrapper get(Object key) { + public ValueWrapper get(Object key) { String stringKey = String.valueOf(key); Object valueObject = map().get(stringKey); return valueObject != null ? new SimpleValueWrapper(valueObject) : null; @@ -73,13 +73,7 @@ public synchronized ValueWrapper get(Object key) { public synchronized T get(Object key, Class type) { String stringKey = String.valueOf(key); Object valueObject = map().get(stringKey); - - if (valueObject != null) { - return type.cast(valueObject); - } - - return type.cast(null); - + return valueObject != null ? type.cast(valueObject) : null; } @Override diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index bb63fc3f4e..491dd2c5de 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -5,10 +5,12 @@ import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import com.netgrif.application.engine.configuration.properties.CacheConfigurationProperties; import com.netgrif.application.engine.files.minio.StorageConfigurationProperties; -import com.netgrif.application.engine.objects.petrinet.domain.*; +import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; +import com.netgrif.application.engine.objects.petrinet.domain.PetriNetSearch; +import com.netgrif.application.engine.objects.petrinet.domain.Transition; +import com.netgrif.application.engine.objects.petrinet.domain.VersionType; import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; import com.netgrif.application.engine.petrinet.web.responsebodies.ArcImportReference; -import com.netgrif.application.engine.objects.auth.domain.Group; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import com.netgrif.application.engine.auth.service.UserService; import com.netgrif.application.engine.elastic.service.interfaces.IElasticPetriNetMappingService; diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java index b0f7317bb5..0b91ddc5c0 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java @@ -55,7 +55,7 @@ public void cachePetriNetFunctions(PetriNet petriNet) { .map(function -> CachedFunction.build(shell, function)) .collect(Collectors.toList()); - Cache namespaceFunctionsCache = cacheManager.getCache(properties.getNamespaceFunctions()); + Cache namespaceFunctionsCache = getRequiredCache(properties.getNamespaceFunctions()); if (!functions.isEmpty()) { evaluateCachedFunctions(functions); @@ -67,7 +67,7 @@ public void cachePetriNetFunctions(PetriNet petriNet) { @Override public void reloadCachedFunctions(String petriNetId) { - cacheManager.getCache(properties.getNamespaceFunctions()).evictIfPresent(petriNetId); + getRequiredCache(properties.getNamespaceFunctions()).evictIfPresent(petriNetId); cachePetriNetFunctions(petriNetService.getNewestVersionByIdentifier(petriNetId)); } @@ -79,7 +79,7 @@ public void reloadCachedFunctions(PetriNet petriNet) { @Override public Closure getCompiledAction(Action action, boolean shouldRewriteCachedActions) { String stringId = action.getId().toString(); - Cache actionsCache = cacheManager.getCache(CacheMapKeys.ACTIONS); + Cache actionsCache = getRequiredCache(CacheMapKeys.ACTIONS); Object nativeActionsCache = actionsCache.getNativeCache(); if (nativeActionsCache instanceof Map map) { @@ -94,7 +94,7 @@ public Closure getCompiledAction(Action action, boolean shouldRewriteCachedActio @Override public List getCachedFunctions(List functions) { List cachedFunctions = new ArrayList<>(functions.size()); - Cache functionsCache = cacheManager.getCache(CacheMapKeys.FUNCTIONS); + Cache functionsCache = getRequiredCache(CacheMapKeys.FUNCTIONS); Object nativeFunctionsCache = functionsCache.getNativeCache(); if (nativeFunctionsCache instanceof Map map) { @@ -147,21 +147,29 @@ private String stringifyParameterTypes(Class[] a) { @Override public Map> getNamespaceFunctionCache() { - return new HashMap<>((Map) cacheManager.getCache(properties.getNamespaceFunctions()).getNativeCache()); + return new HashMap<>((Map) getRequiredCache(properties.getNamespaceFunctions()).getNativeCache()); } @Override public void clearActionCache() { - cacheManager.getCache(CacheMapKeys.ACTIONS).clear(); + getRequiredCache(CacheMapKeys.ACTIONS).clear(); } @Override public void clearNamespaceFunctionCache() { - cacheManager.getCache(properties.getNamespaceFunctions()).clear(); + getRequiredCache(properties.getNamespaceFunctions()).clear(); } @Override public void clearFunctionCache() { - cacheManager.getCache(CacheMapKeys.FUNCTIONS).clear(); + getRequiredCache(CacheMapKeys.FUNCTIONS).clear(); + } + + private Cache getRequiredCache(String name) { + Cache cache = cacheManager.getCache(name); + if (cache == null) { + throw new IllegalStateException("Cache '" + name + "' is not configured"); + } + return cache; } } From 6e09ba1a543f1929581cf2f18f96f69e701cb6d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Voz=C3=A1r?= Date: Thu, 25 Sep 2025 22:01:48 +0200 Subject: [PATCH 11/40] NAE-2212 - repair of PR, rework to use petriNetService and other --- .../configuration/CacheConfiguration.java | 5 +++- .../configuration/cache/CacheMapKeys.java | 5 ++-- .../cache/WorkerConcurrentCacheManager.java | 16 ++++++++-- .../service/FieldActionsCacheService.java | 29 +++++++++++-------- 4 files changed, 38 insertions(+), 17 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java index 685aab2a96..40ebcaf45d 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java @@ -1,5 +1,6 @@ package com.netgrif.application.engine.configuration; +import com.netgrif.application.engine.configuration.cache.CacheMapKeys; import com.netgrif.application.engine.configuration.cache.WorkerConcurrentCacheManager; import com.netgrif.application.engine.configuration.properties.CacheConfigurationProperties; import org.springframework.cache.CacheManager; @@ -10,6 +11,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; +import java.util.List; + @Configuration @EnableCaching public class CacheConfiguration extends CachingConfigurerSupport { @@ -24,7 +27,7 @@ public CacheConfiguration(CacheConfigurationProperties properties) { @Primary @Override public CacheManager cacheManager() { - return new WorkerConcurrentCacheManager(properties.getAllCaches().toArray(String[]::new)); + return new WorkerConcurrentCacheManager(List.of(CacheMapKeys.NAMESPACE_FUNCTIONS), properties.getAllCaches().toArray(String[]::new)); } @Bean diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/CacheMapKeys.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/CacheMapKeys.java index e6c776944d..8949eb2bf4 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/CacheMapKeys.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/CacheMapKeys.java @@ -1,5 +1,6 @@ package com.netgrif.application.engine.configuration.cache; -public class CacheMapKeys { - public static final String NAMESPACE_FUNCTIONS = "namespaceFunctionsCache"; +public final class CacheMapKeys { + private CacheMapKeys() {} + public static final String NAMESPACE_FUNCTIONS = "namespaceFunctions"; } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java index 6b8bd5fc0c..831a1bc7d0 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java @@ -3,19 +3,31 @@ import org.springframework.cache.Cache; import org.springframework.cache.concurrent.ConcurrentMapCacheManager; +import java.util.ArrayList; +import java.util.List; + public class WorkerConcurrentCacheManager extends ConcurrentMapCacheManager { + private List readOnlyCacheNames; + public WorkerConcurrentCacheManager() { super(); + this.readOnlyCacheNames = new ArrayList<>(); + } + + public WorkerConcurrentCacheManager(List readOnlyCacheNames) { + super(); + this.readOnlyCacheNames = readOnlyCacheNames; } - public WorkerConcurrentCacheManager(String... cacheNames) { + public WorkerConcurrentCacheManager(List readOnlyCacheNames, String... cacheNames) { super(cacheNames); + this.readOnlyCacheNames = readOnlyCacheNames; } @Override protected Cache createConcurrentMapCache(String name) { Cache base = super.createConcurrentMapCache(name); - if (CacheMapKeys.NAMESPACE_FUNCTIONS.equals(name)) { + if (readOnlyCacheNames.contains(name)) { return new ReadOnlyCache(base); } return base; diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java index 5f158efb7c..51432b96c8 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java @@ -15,8 +15,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import java.util.ArrayList; @@ -24,7 +25,6 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import java.util.stream.Stream; @Service @Slf4j @@ -33,15 +33,13 @@ public class FieldActionsCacheService implements IFieldActionsCacheService { private final RunnerConfigurationProperties.FieldRunnerProperties properties; private IPetriNetService petriNetService; - private MongoTemplate mongoTemplate; private Map actionsCache; private Map> namespaceFunctionsCache; private Map functionsCache; private final GroovyShell shell; - public FieldActionsCacheService(RunnerConfigurationProperties.FieldRunnerProperties properties, IGroovyShellFactory shellFactory, MongoTemplate mongoTemplate) { + public FieldActionsCacheService(RunnerConfigurationProperties.FieldRunnerProperties properties, IGroovyShellFactory shellFactory) { this.properties = properties; - this.mongoTemplate = mongoTemplate; this.actionsCache = new MaxSizeHashMap<>(properties.getActionCacheSize()); this.functionsCache = new MaxSizeHashMap<>(properties.getFunctionsCacheSize()); this.namespaceFunctionsCache = new MaxSizeHashMap<>(properties.getNamespaceCacheSize()); @@ -102,16 +100,23 @@ public List getCachedFunctions(List stream = mongoTemplate.query(PetriNet.class).matching(query).stream()) { - stream.forEach(petriNet -> { - if (petriNet == null) return; + Pageable pageable = PageRequest.of(0, 500); + Page page = petriNetService.getAll(pageable); + + while (!page.isEmpty()) { + for (PetriNet petriNet : page) { try { - this.cachePetriNetFunctions(petriNet); + cachePetriNetFunctions(petriNet); } catch (Exception e) { log.warn("Failed to cache functions for PetriNet id={}", petriNet.getStringId(), e); } - }); + } + + if (!page.hasNext()) { + break; + } + pageable = pageable.next(); + page = petriNetService.getAll(pageable); } } From 790659b4887bdf09dca96e3cdfe2704e484c70ce Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 29 Sep 2025 15:46:55 +0200 Subject: [PATCH 12/40] - test fix --- .../configuration/cache/WorkerConcurrentCacheManager.java | 2 +- nae-object-library/src/main/resources/petriflow_schema.xsd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java index 831a1bc7d0..3d43971c51 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java @@ -27,7 +27,7 @@ public WorkerConcurrentCacheManager(List readOnlyCacheNames, String... c @Override protected Cache createConcurrentMapCache(String name) { Cache base = super.createConcurrentMapCache(name); - if (readOnlyCacheNames.contains(name)) { + if (readOnlyCacheNames != null && readOnlyCacheNames.contains(name)) { return new ReadOnlyCache(base); } return base; diff --git a/nae-object-library/src/main/resources/petriflow_schema.xsd b/nae-object-library/src/main/resources/petriflow_schema.xsd index 28e88a26d6..14cd208484 100644 --- a/nae-object-library/src/main/resources/petriflow_schema.xsd +++ b/nae-object-library/src/main/resources/petriflow_schema.xsd @@ -3,6 +3,6 @@ - + \ No newline at end of file From dd80913b657e65ca418f676b36d7fe9cba5cd5bb Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 30 Sep 2025 11:30:36 +0200 Subject: [PATCH 13/40] - test fix --- nae-object-library/src/main/resources/petriflow_schema.xsd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nae-object-library/src/main/resources/petriflow_schema.xsd b/nae-object-library/src/main/resources/petriflow_schema.xsd index 14cd208484..28e88a26d6 100644 --- a/nae-object-library/src/main/resources/petriflow_schema.xsd +++ b/nae-object-library/src/main/resources/petriflow_schema.xsd @@ -3,6 +3,6 @@ - + \ No newline at end of file From 7dc79472f686ed8a24447745a68e0c0afb32bde5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Voz=C3=A1r?= Date: Tue, 30 Sep 2025 11:43:05 +0200 Subject: [PATCH 14/40] [NAE-2212] Change list to set, remove CacheMapKeys, use copyOf for safety --- .../configuration/CacheConfiguration.java | 6 +++--- .../configuration/cache/CacheMapKeys.java | 6 ------ .../cache/WorkerConcurrentCacheManager.java | 20 +++++++++++-------- 3 files changed, 15 insertions(+), 17 deletions(-) delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/CacheMapKeys.java diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java index 40ebcaf45d..7fed1391bb 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java @@ -1,6 +1,5 @@ package com.netgrif.application.engine.configuration; -import com.netgrif.application.engine.configuration.cache.CacheMapKeys; import com.netgrif.application.engine.configuration.cache.WorkerConcurrentCacheManager; import com.netgrif.application.engine.configuration.properties.CacheConfigurationProperties; import org.springframework.cache.CacheManager; @@ -11,7 +10,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; -import java.util.List; +import java.util.Set; + @Configuration @EnableCaching @@ -27,7 +27,7 @@ public CacheConfiguration(CacheConfigurationProperties properties) { @Primary @Override public CacheManager cacheManager() { - return new WorkerConcurrentCacheManager(List.of(CacheMapKeys.NAMESPACE_FUNCTIONS), properties.getAllCaches().toArray(String[]::new)); + return new WorkerConcurrentCacheManager(Set.of(properties.getNamespaceFunctions()), properties.getAllCaches().toArray(String[]::new)); } @Bean diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/CacheMapKeys.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/CacheMapKeys.java deleted file mode 100644 index 8949eb2bf4..0000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/CacheMapKeys.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.netgrif.application.engine.configuration.cache; - -public final class CacheMapKeys { - private CacheMapKeys() {} - public static final String NAMESPACE_FUNCTIONS = "namespaceFunctions"; -} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java index 3d43971c51..ffa8cd3c30 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java @@ -3,25 +3,29 @@ import org.springframework.cache.Cache; import org.springframework.cache.concurrent.ConcurrentMapCacheManager; -import java.util.ArrayList; -import java.util.List; +import java.util.Objects; +import java.util.Set; public class WorkerConcurrentCacheManager extends ConcurrentMapCacheManager { - private List readOnlyCacheNames; + private final Set readOnlyCacheNames; public WorkerConcurrentCacheManager() { super(); - this.readOnlyCacheNames = new ArrayList<>(); + this.readOnlyCacheNames = Set.of(); } - public WorkerConcurrentCacheManager(List readOnlyCacheNames) { + public WorkerConcurrentCacheManager(Set readOnlyCacheNames) { super(); - this.readOnlyCacheNames = readOnlyCacheNames; + this.readOnlyCacheNames = Set.copyOf( + Objects.requireNonNull(readOnlyCacheNames, "readOnlyCacheNames must not be null.") + ); } - public WorkerConcurrentCacheManager(List readOnlyCacheNames, String... cacheNames) { + public WorkerConcurrentCacheManager(Set readOnlyCacheNames, String... cacheNames) { super(cacheNames); - this.readOnlyCacheNames = readOnlyCacheNames; + this.readOnlyCacheNames = Set.copyOf( + Objects.requireNonNull(readOnlyCacheNames, "readOnlyCacheNames must not be null.") + ); } @Override From 93921f92e182d4e5305387c6c0235087742228b2 Mon Sep 17 00:00:00 2001 From: Machac Date: Tue, 30 Sep 2025 18:09:15 +0200 Subject: [PATCH 15/40] fix --- .github/workflows/pr-build.yml | 2 +- application-engine/docker-compose.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index ee7b621b30..f03f97497a 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -52,7 +52,7 @@ jobs: options: -e="discovery.type=single-node" -e="xpack.security.enabled=false" --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10 minio: - image: minio/minio:RELEASE.2022-12-12T19-27-27Z + image: docker.io/bawix/minio:2022 ports: - 9000:9000 - 9001:9001 diff --git a/application-engine/docker-compose.yml b/application-engine/docker-compose.yml index b002ee4b82..3b0d9dc215 100644 --- a/application-engine/docker-compose.yml +++ b/application-engine/docker-compose.yml @@ -43,6 +43,7 @@ services: ports: - '9000:9000' - '9001:9001' + command: server /data --console-address ":9001" networks: - minionetwork volumes: From fc76808fe79fbb2cba618a112b8771e790c8b94d Mon Sep 17 00:00:00 2001 From: chvostek Date: Wed, 19 Nov 2025 12:13:55 +0100 Subject: [PATCH 16/40] [NAE-2266] Introduce active version of process - add attribute isVersionActive in PetriNet - introduce PetriNetService.getActiveVersionByIdentifier - rename PetriNetService.getNewestVersionByIdentifier to PetriNetService.getLatestVersionByIdentifier - switch usage of getNewestVersionByIdentifier to getActiveVersionByIdentifier - enhance process import validations --- .../logic/action/ActionDelegate.groovy | 2 +- .../action/delegate/RoleActionDelegate.groovy | 4 +- .../engine/startup/ImportHelper.groovy | 4 +- .../CacheConfigurationProperties.java | 12 +- .../services/DashboardItemServiceImpl.java | 2 +- .../DashboardManagementServiceImpl.java | 2 +- .../repositories/PetriNetRepository.java | 5 + .../petrinet/service/PetriNetService.java | 111 +++++++++++++----- .../service/interfaces/IPetriNetService.java | 15 ++- .../startup/runner/DefaultFiltersRunner.java | 2 +- .../startup/runner/ImpersonationRunner.java | 2 +- .../workflow/service/CaseSearchService.java | 2 +- .../service/FieldActionsCacheService.java | 2 +- .../service/FilterImportExportService.java | 2 +- .../service/MenuImportExportService.java | 4 +- .../workflow/service/WorkflowService.java | 6 +- .../export/service/ExportServiceTest.groovy | 4 +- .../ImpersonationServiceTest.groovy | 2 +- .../petrinet/domain/ImporterTest.groovy | 4 +- .../service/CachePetriNetServiceTest.groovy | 2 +- .../engine/workflow/NewInitTest.groovy | 2 +- .../change_caseref_value_action_test.xml | 4 +- .../petriNets/NAE-1290_Export_actions.xml | 2 +- .../elastic/domain/ElasticPetriNet.java | 4 + .../objects/petrinet/domain/PetriNet.java | 50 +++++--- .../petrinet/domain/version/Version.java | 20 ++++ .../spring/petrinet/domain/PetriNet.java | 1 + 27 files changed, 190 insertions(+), 82 deletions(-) diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 3331909650..25b6eda70b 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -2439,7 +2439,7 @@ class ActionDelegate { return [(role.importId + ":" + GLOBAL_ROLE), ("$role.name (🌍 Global role)" as String)] } else { if (!temp.containsKey(entry.value)) { - temp.put(entry.value, petriNetService.getNewestVersionByIdentifier(entry.value)) + temp.put(entry.value, petriNetService.getActiveVersionByIdentifier(entry.value)) } PetriNet net = temp[entry.value] ProcessRole role = net.roles.find { it.value.importId == entry.key }.value diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy index 78f640b705..da2f0806ff 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy @@ -51,7 +51,7 @@ class RoleActionDelegate extends AbstractActionDelegate { } AbstractUser assignRole(String roleImportId, String petriNetIdentifier, AbstractUser user = affectedUser) { - PetriNet petriNet = petriNetService.getNewestVersionByIdentifier(petriNetIdentifier) + PetriNet petriNet = petriNetService.getActiveVersionByIdentifier(petriNetIdentifier) assignRole(roleImportId, user, petriNet) } @@ -75,7 +75,7 @@ class RoleActionDelegate extends AbstractActionDelegate { } AbstractUser removeRole(String roleImportId, String petriNetIdentifier, AbstractUser user = affectedUser) { - PetriNet petriNet = petriNetService.getNewestVersionByIdentifier(petriNetIdentifier) + PetriNet petriNet = petriNetService.getActiveVersionByIdentifier(petriNetIdentifier) removeRole(roleImportId, user, petriNet) } diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy index 651bf31abb..ed89eeb78f 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy @@ -128,7 +128,7 @@ class ImportHelper { } Optional upsertNet(String filename, String identifier, VersionType release = VersionType.MAJOR, LoggedUser author = ActorTransformer.toLoggedUser(userService.getSystem())) { - PetriNet petriNet = petriNetService.getNewestVersionByIdentifier(identifier) + PetriNet petriNet = petriNetService.getActiveVersionByIdentifier(identifier) if (!petriNet) { return createNet(filename, release, author) } @@ -249,7 +249,7 @@ class ImportHelper { } Optional importProcessOnce(String message, String netIdentifier, String netFileName) { - PetriNet filter = petriNetService.getNewestVersionByIdentifier(netIdentifier) + PetriNet filter = petriNetService.getActiveVersionByIdentifier(netIdentifier) if (filter != null) { log.info("${message} has already been imported.") return Optional.of(filter) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java index 8050c7ac37..ad32548f84 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java @@ -27,9 +27,14 @@ public class CacheConfigurationProperties { private String petriNetByIdentifier = "petriNetByIdentifier"; /** - * Default cache name for caching the newest versions of Petri nets. + * Default cache name for caching the active versions of Petri nets. */ - private String petriNetNewest = "petriNetNewest"; + private String petriNetActive = "petriNetActive"; + + /** + * Default cache name for caching the latest versions of Petri nets. + */ + private String petriNetLatest = "petriNetLatest"; /** * Default cache name for general Petri net caching. @@ -54,7 +59,8 @@ public class CacheConfigurationProperties { * @return a {@link Set} of all cache names. */ public Set getAllCaches() { - Set caches = new LinkedHashSet<>(Arrays.asList(petriNetById, petriNetByIdentifier, petriNetNewest, petriNetCache, loadedModules)); + Set caches = new LinkedHashSet<>(Arrays.asList(petriNetById, petriNetByIdentifier, petriNetActive, + petriNetLatest, petriNetCache, loadedModules)); caches.addAll(additional); return caches; } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java b/application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java index c0a5627de0..8d40f6cb91 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java @@ -62,7 +62,7 @@ public Case getOrCreate(DashboardItemBody body) throws TransitionNotExecutableEx } LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedOrSystem()); - itemCase = workflowService.createCase(petriNetService.getNewestVersionByIdentifier(DashboardItemConstants.PROCESS_IDENTIFIER).getStringId(), body.getName().getDefaultValue(), "", loggedUser).getCase(); + itemCase = workflowService.createCase(petriNetService.getActiveVersionByIdentifier(DashboardItemConstants.PROCESS_IDENTIFIER).getStringId(), body.getName().getDefaultValue(), "", loggedUser).getCase(); ToDataSetOutcome outcome = body.toDataSet(); itemCase = setDataWithExecute(itemCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); return itemCase; diff --git a/application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java b/application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java index 95a426e88e..a449783116 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java @@ -64,7 +64,7 @@ public Case createDashboardManagement(DashboardManagementBody body) throws Trans } addReferencedMenuItems(body); LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedOrSystem()); - managementCase = workflowService.createCase(petriNetService.getNewestVersionByIdentifier(DashboardManagementConstants.PROCESS_IDENTIFIER).getStringId(), body.getName().getDefaultValue(), "", loggedUser).getCase(); + managementCase = workflowService.createCase(petriNetService.getActiveVersionByIdentifier(DashboardManagementConstants.PROCESS_IDENTIFIER).getStringId(), body.getName().getDefaultValue(), "", loggedUser).getCase(); ToDataSetOutcome outcome = body.toDataSet(); managementCase = setDataWithExecute(managementCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); return managementCase; diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java index a8dd005b20..d46394681b 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java @@ -33,6 +33,11 @@ public interface PetriNetRepository extends MongoRepository, Q */ PetriNet findByIdentifierAndVersion(String identifier, Version version); + /** + * todo javadoc + */ + PetriNet findByIdentifierAndVersionActive(String identifier, boolean isVersionActive); + /** * Finds a paginated list of {@link PetriNet} entities by their identifier. * diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index a468461636..8dc3bcff8f 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -7,7 +7,6 @@ import com.netgrif.application.engine.files.minio.StorageConfigurationProperties; import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; import com.netgrif.application.engine.petrinet.web.responsebodies.ArcImportReference; -import com.netgrif.application.engine.objects.auth.domain.Group; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import com.netgrif.application.engine.auth.service.UserService; import com.netgrif.application.engine.elastic.service.interfaces.IElasticPetriNetMappingService; @@ -141,15 +140,16 @@ protected Importer getImporter() { @Override public void evictAllCaches() { requireNonNull(cacheManager.getCache(cacheProperties.getPetriNetById()), cacheProperties.getPetriNetById()).clear(); - requireNonNull(cacheManager.getCache(cacheProperties.getPetriNetNewest()), cacheProperties.getPetriNetNewest()).clear(); + requireNonNull(cacheManager.getCache(cacheProperties.getPetriNetActive()), cacheProperties.getPetriNetActive()).clear(); + requireNonNull(cacheManager.getCache(cacheProperties.getPetriNetLatest()), cacheProperties.getPetriNetLatest()).clear(); requireNonNull(cacheManager.getCache(cacheProperties.getPetriNetCache()), cacheProperties.getPetriNetCache()).clear(); requireNonNull(cacheManager.getCache(cacheProperties.getPetriNetByIdentifier()), cacheProperties.getPetriNetByIdentifier()).clear(); - } public void evictCache(PetriNet net) { requireNonNull(cacheManager.getCache(cacheProperties.getPetriNetById()), cacheProperties.getPetriNetById()).evict(net.getStringId()); - requireNonNull(cacheManager.getCache(cacheProperties.getPetriNetNewest()), cacheProperties.getPetriNetNewest()).evict(net.getIdentifier()); + requireNonNull(cacheManager.getCache(cacheProperties.getPetriNetActive()), cacheProperties.getPetriNetActive()).evict(net.getIdentifier()); + requireNonNull(cacheManager.getCache(cacheProperties.getPetriNetLatest()), cacheProperties.getPetriNetLatest()).evict(net.getIdentifier()); requireNonNull(cacheManager.getCache(cacheProperties.getPetriNetCache()), cacheProperties.getPetriNetCache()).evict(net.getObjectId()); requireNonNull(cacheManager.getCache(cacheProperties.getPetriNetByIdentifier()), cacheProperties.getPetriNetByIdentifier()).evict(net.getIdentifier() + net.getVersion().toString()); } @@ -193,38 +193,67 @@ public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionTyp ImportPetriNetEventOutcome outcome = new ImportPetriNetEventOutcome(); ByteArrayOutputStream xmlCopy = new ByteArrayOutputStream(); IOUtils.copy(xmlFile, xmlCopy); - Optional imported = getImporter().importPetriNet(new ByteArrayInputStream(xmlCopy.toByteArray())); - if (imported.isEmpty()) { + Optional importedProcess = getImporter().importPetriNet(new ByteArrayInputStream(xmlCopy.toByteArray())); + if (importedProcess.isEmpty()) { return outcome; } - PetriNet net = imported.get(); - - PetriNet existingNet = getNewestVersionByIdentifier(net.getIdentifier()); + PetriNet newProcess = importedProcess.get(); + PetriNet inactivatedProcess = null; - if (existingNet != null) { - if (existingNet.getVersion().equals(net.getVersion())) { - throw new IllegalArgumentException("Provided Petri net version is already present in the system"); + if (self.getPetriNet(newProcess.getIdentifier(), newProcess.getVersion()) != null) { + throw new IllegalArgumentException("A process [%s] with such version [%s] already exists" + .formatted(newProcess.getIdentifier(), newProcess.getVersion())); + } + PetriNet existingLatestProcess = self.getLatestVersionByIdentifier(newProcess.getIdentifier()); + if (existingLatestProcess == null) { + newProcess.setVersion(new Version()); + } else { + if (newProcess.getVersion() == null) { + newProcess.setVersion(existingLatestProcess.getVersion()); + newProcess.incrementVersion(releaseType); + } else if (newProcess.getVersion().isLowerThan(existingLatestProcess.getVersion())) { + throw new IllegalArgumentException("Only higher versions of process [%s] are allowed to import. The requested version [%s] cannot be imported because the higher version [%s] already exists." + .formatted(newProcess.getIdentifier(), newProcess.getVersion(), existingLatestProcess.getVersion())); } - if (net.getVersion() == null) { - net.setVersion(existingNet.getVersion()); - net.incrementVersion(releaseType); + if (existingLatestProcess.isVersionActive()) { + existingLatestProcess.makeInactive(); + inactivatedProcess = existingLatestProcess; + } else { + PetriNet existingActiveProcess = self.getActiveVersionByIdentifier(newProcess.getIdentifier()); + if (existingActiveProcess != null) { + existingActiveProcess.makeInactive(); + inactivatedProcess = existingActiveProcess; + } } - } else if (net.getVersion() == null) { - net.setVersion(new Version()); } + newProcess.makeActive(); - processRoleService.saveAll(net.getRoles().values()); - net.setAuthor(ActorTransformer.toActorRef(author)); - functionCacheService.cachePetriNetFunctions(net); - Path savedPath = getImporter().saveNetFile(net, new ByteArrayInputStream(xmlCopy.toByteArray())); + processRoleService.saveAll(newProcess.getRoles().values()); + newProcess.setAuthor(ActorTransformer.toActorRef(author)); + functionCacheService.cachePetriNetFunctions(newProcess); + Path savedPath = getImporter().saveNetFile(newProcess, new ByteArrayInputStream(xmlCopy.toByteArray())); xmlCopy.close(); - log.info("Petri net " + net.getTitle() + " (" + net.getInitials() + " v" + net.getVersion() + ") imported successfully and saved in a folder: " + savedPath.toString()); + log.info("Petri newProcess " + newProcess.getTitle() + " (" + newProcess.getInitials() + " v" + newProcess.getVersion() + ") importedProcess successfully and saved in a folder: " + savedPath.toString()); - outcome.setOutcomes(eventService.runActions(net.getPreUploadActions(), null, Optional.empty(), params)); + outcome.setOutcomes(eventService.runActions(newProcess.getPreUploadActions(), null, Optional.empty(), params)); publisher.publishEvent(new ProcessDeployEvent(outcome, EventPhase.PRE)); - save(net); - outcome.setOutcomes(eventService.runActions(net.getPostUploadActions(), null, Optional.empty(), params)); - outcome.setNet(imported.get()); + if (inactivatedProcess != null) { + save(inactivatedProcess); + } + try { + save(newProcess); + } catch (Exception rethrow) { + if (inactivatedProcess != null) { + // make sure there is always an active version + log.warn("Something unexpected happened while saving new version of process [{}]. Rolling back status of a version, that have been inactivated...", + newProcess.getIdentifier()); + inactivatedProcess.makeActive(); + save(inactivatedProcess); + } + throw rethrow; + } + outcome.setOutcomes(eventService.runActions(newProcess.getPostUploadActions(), null, Optional.empty(), params)); + outcome.setNet(importedProcess.get()); publisher.publishEvent(new ProcessDeployEvent(outcome, EventPhase.POST)); return outcome; } @@ -263,6 +292,9 @@ public PetriNet getPetriNet(String id) { @Override @Cacheable(value = "petriNetByIdentifier", key = "#identifier+#version.toString()", unless = "#result == null") public PetriNet getPetriNet(String identifier, Version version) { + if (identifier == null || version == null) { + return null; + } PetriNet net = repository.findByIdentifierAndVersion(identifier, version); if (net == null) { return null; @@ -284,13 +316,28 @@ public List findAllById(List ids) { } @Override - @Cacheable(value = "petriNetNewest", unless = "#result == null") - public PetriNet getNewestVersionByIdentifier(String identifier) { - List nets = repository.findByIdentifier(identifier, PageRequest.of(0, 1, Sort.Direction.DESC, "version.major", "version.minor", "version.patch")).getContent(); - if (nets.isEmpty()) { + @Cacheable(value = "petriNetActive", unless = "#result == null") + public PetriNet getActiveVersionByIdentifier(String identifier) { + // todo 2266 test + if (identifier == null) { + return null; + } + return repository.findByIdentifierAndVersionActive(identifier, true); + } + + @Override + @Cacheable(value = "petriNetLatest", unless = "#result == null") + public PetriNet getLatestVersionByIdentifier(String identifier) { + // todo 2266 test + if (identifier == null) { + return null; + } + List processes = repository.findByIdentifier(identifier, PageRequest.of(0, 1, + Sort.Direction.DESC, "version.major", "version.minor", "version.patch")).getContent(); + if (processes.isEmpty()) { return null; } - return nets.getFirst(); + return processes.getFirst(); } /** @@ -422,7 +469,7 @@ public List getReferencesByUsersProcessRoles(LoggedUser user, @Override public PetriNetReference getReference(String identifier, Version version, LoggedUser user, Locale locale) { - PetriNet net = version == null ? getNewestVersionByIdentifier(identifier) : getPetriNet(identifier, version); + PetriNet net = version == null ? getActiveVersionByIdentifier(identifier) : getPetriNet(identifier, version); return net != null ? transformToReference(net, locale) : new PetriNetReference(); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java index c6fd966cb5..8988afd566 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java @@ -153,12 +153,21 @@ static DataFieldReference transformToReference(PetriNet net, Transition transiti List findAllById(List ids); /** - * Retrieves the newest version of a {@link PetriNet} by its identifier. + * Retrieves the active version of a {@link PetriNet} by its identifier. + * todo javadoc isversionactive logic * * @param identifier the unique identifier of the PetriNet - * @return the newest version of the {@link PetriNet} matching the provided identifier + * @return the active version of the {@link PetriNet} matching the provided identifier */ - PetriNet getNewestVersionByIdentifier(String identifier); + PetriNet getActiveVersionByIdentifier(String identifier); + + /** + * Retrieves the latest version of a {@link PetriNet} by its identifier. + * + * @param identifier the unique identifier of the PetriNet + * @return the latest version of the {@link PetriNet} matching the provided identifier + */ + PetriNet getLatestVersionByIdentifier(String identifier); /** * Retrieves a paginated list of all {@link PetriNet} objects. diff --git a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java index 93a2618d03..64453b2c15 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java @@ -424,7 +424,7 @@ public Optional createFilter(String title, String icon, String filterType, } private Optional createFilterCase(String title, String icon, String filterType, String filterVisibility, String filterQuery, List allowedNets, Map filterMetadata, Map titleTranslations, String originId, boolean viewOrigin, boolean isImported) { - PetriNet filterNet = this.petriNetService.getNewestVersionByIdentifier("filter"); + PetriNet filterNet = this.petriNetService.getActiveVersionByIdentifier("filter"); if (filterNet == null) { return Optional.empty(); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/ImpersonationRunner.java b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/ImpersonationRunner.java index 44522a2556..6407d1e001 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/ImpersonationRunner.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/ImpersonationRunner.java @@ -39,7 +39,7 @@ public void createConfigNets() { } public Optional importProcess(final String message, String netIdentifier, String netFileName) { - PetriNet foundNet = petriNetService.getNewestVersionByIdentifier(netIdentifier); + PetriNet foundNet = petriNetService.getActiveVersionByIdentifier(netIdentifier); if (foundNet != null) { log.info("{} has already been imported.", message); return Optional.of(foundNet); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java index 923d17bfa5..c4769d6565 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java @@ -280,7 +280,7 @@ public Predicate fullText(Object petriNetQuery, String searchPhrase) { // TODO JOFO: unpaged necessary? petriNets = petriNetService.getAll(Pageable.unpaged()).getContent(); } else { - petriNets = processes.stream().map(process -> petriNetService.getNewestVersionByIdentifier(process)).collect(Collectors.toList()); + petriNets = processes.stream().map(process -> petriNetService.getActiveVersionByIdentifier(process)).collect(Collectors.toList()); } if (petriNets.isEmpty()) return null; diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java index 1b996bc60d..5ae423c476 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java @@ -71,7 +71,7 @@ public void cachePetriNetFunctions(PetriNet petriNet) { @Override public void reloadCachedFunctions(PetriNet petriNet) { namespaceFunctionsCache.remove(petriNet.getIdentifier()); - cachePetriNetFunctions(petriNetService.getNewestVersionByIdentifier(petriNet.getIdentifier())); + cachePetriNetFunctions(petriNetService.getActiveVersionByIdentifier(petriNet.getIdentifier())); } @Override diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java index 9dd6cea3f5..54dfed8b6c 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java @@ -266,7 +266,7 @@ public void changeFilterField(Collection filterFields) { filterFields.forEach(f -> { Task importedFilterTask = taskService.findOne(f); Case filterCase = workflowService.findOne(importedFilterTask.getCaseId()); - PetriNet filterNet = petriNetService.getNewestVersionByIdentifier(FILTER_NET_IDENTIFIER); + PetriNet filterNet = petriNetService.getActiveVersionByIdentifier(FILTER_NET_IDENTIFIER); List requiredNets = filterCase.getDataSet().get(FIELD_FILTER).getAllowedNets(); List currentNets = petriNetService.getExistingPetriNetIdentifiersFromIdentifiersList(requiredNets); if (currentNets.size() < requiredNets.size()) { diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java index 1eb921882b..4766834b65 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java @@ -237,7 +237,7 @@ public String createMenuItemCase(StringBuilder resultMessage, MenuEntry item, St String roleImportId = menuEntryRole.getRoleImportId(); String netImportId = menuEntryRole.getNetImportId(); if (netImportId != null) { - PetriNet net = petriNetService.getNewestVersionByIdentifier(netImportId); + PetriNet net = petriNetService.getActiveVersionByIdentifier(netImportId); if (net == null) { resultMessage.append("\n- Missing net with import ID: \"").append(netImportId).append("\"").append("for role ").append(roleImportId).append("\n"); netCheck.set(false); @@ -261,7 +261,7 @@ public String createMenuItemCase(StringBuilder resultMessage, MenuEntry item, St } //Creating new Case of preference_filter_item net and setting its data... Case menuItemCase = workflowService.createCase( - petriNetService.getNewestVersionByIdentifier("preference_filter_item").getStringId(), + petriNetService.getActiveVersionByIdentifier("preference_filter_item").getStringId(), item.getEntryName() + "_" + menuIdentifier, "", ActorTransformer.toLoggedUser(userService.getSystem()) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java index 9fdb0e83f1..c4e9bae29a 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java @@ -285,7 +285,7 @@ public CreateCaseEventOutcome createCase(String netId, String title, String colo @Override public CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, LoggedUser user, Locale locale, Map params) { - PetriNet net = petriNetService.getNewestVersionByIdentifier(identifier); + PetriNet net = petriNetService.getActiveVersionByIdentifier(identifier); if (net == null) { throw new IllegalArgumentException("Petri net with identifier [" + identifier + "] does not exist."); } @@ -299,7 +299,7 @@ public CreateCaseEventOutcome createCaseByIdentifier(String identifier, String t @Override public CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, LoggedUser user, Map params) { - PetriNet net = petriNetService.getNewestVersionByIdentifier(identifier); + PetriNet net = petriNetService.getActiveVersionByIdentifier(identifier); if (net == null) { throw new IllegalArgumentException("Petri net with identifier [" + identifier + "] does not exist."); } @@ -308,7 +308,7 @@ public CreateCaseEventOutcome createCaseByIdentifier(String identifier, String t @Override public CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, LoggedUser user) { - PetriNet net = petriNetService.getNewestVersionByIdentifier(identifier); + PetriNet net = petriNetService.getActiveVersionByIdentifier(identifier); if (net == null) { throw new IllegalArgumentException("Petri net with identifier [" + identifier + "] does not exist."); } diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy index dab63cbf0f..578ba26f42 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy @@ -128,7 +128,7 @@ class ExportServiceTest { taskService.assignTask(ActorTransformer.toLoggedUser(userService.findByEmail(UserConstants.ADMIN_USER_EMAIL, null)), exportTask) Thread.sleep(20000) //Elastic wait - def processId = petriNetService.getNewestVersionByIdentifier("export_test").stringId + def processId = petriNetService.getActiveVersionByIdentifier("export_test").stringId def taskRequest = new ElasticTaskSearchRequest() taskRequest.process = [new com.netgrif.application.engine.workflow.web.requestbodies.taskSearch.PetriNet(processId)] as List taskRequest.transitionId = ["t4"] as List @@ -146,7 +146,7 @@ class ExportServiceTest { @Test void buildDefaultCsvTaskHeaderTest(){ - def processId = petriNetService.getNewestVersionByIdentifier("export_test").stringId + def processId = petriNetService.getActiveVersionByIdentifier("export_test").stringId String exportTask = mainCase.tasks.find { it.transition == "t4" }.task taskService.assignTask(ActorTransformer.toLoggedUser(userService.findByEmail(UserConstants.ADMIN_USER_EMAIL, null)), exportTask) List task = taskRepository.findAll(QTask.task.processId.eq(processId).and(QTask.task.transitionId.eq("t4"))) diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy index b6b97f5352..7eeebba6a5 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy @@ -311,7 +311,7 @@ class ImpersonationServiceTest { } def createConfigCase(AbstractUser user, String impersonator, List roles = null, List auths = null) { - def caze = helper.createCase("config", petriNetService.getNewestVersionByIdentifier(ImpersonationRunner.IMPERSONATION_CONFIG_PETRI_NET_IDENTIFIER)) + def caze = helper.createCase("config", petriNetService.getActiveVersionByIdentifier(ImpersonationRunner.IMPERSONATION_CONFIG_PETRI_NET_IDENTIFIER)) def owner = new UserFieldValue(user) caze.dataSet["impersonated"].value = owner caze.dataSet["impersonated_email"].value = owner.username diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovy index 15e6138b78..33645198c1 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovy @@ -130,7 +130,7 @@ class ImporterTest { assert net.places.size() == 0 // ASSERT IMPORTED NET FROM REPO - net = petriNetService.getNewestVersionByIdentifier("new_model") + net = petriNetService.getActiveVersionByIdentifier("new_model") assert net != null assert net.importId == "new_model" assert net.version.major == 1 @@ -204,7 +204,7 @@ class ImporterTest { assert net2.places.size() == 0 // ASSERT NEW NET FROM REPO - net2 = petriNetService.getNewestVersionByIdentifier("new_model") + net2 = petriNetService.getActiveVersionByIdentifier("new_model") assert net2 != null assert net2.importId == "new_model" assert net2.version.major == 2 diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy index 1c4a7d430a..2f52400152 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy @@ -73,7 +73,7 @@ class CachePetriNetServiceTest { PetriNet testNet = testNetOptional.getNet() assert cacheManager.getCache(cacheProperties.getPetriNetNewest()).get(testNet.getIdentifier()) == null - PetriNet test = petriNetService.getNewestVersionByIdentifier(testNet.getIdentifier()) + PetriNet test = petriNetService.getActiveVersionByIdentifier(testNet.getIdentifier()) assert cacheManager.getCache(cacheProperties.getPetriNetNewest()).get(testNet.getIdentifier()).get().equals(test) } } diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy index 3e7f28bd4d..84d2de4bdc 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy @@ -45,7 +45,7 @@ class NewInitTest { @Test void newInitTest() throws IOException, MissingIconKeyException, MissingPetriNetMetaDataException { petriNetService.importPetriNet(new FileInputStream("src/test/resources/petriNets/nae_1276_Init_value_as_choice.xml"), VersionType.MAJOR, superCreator.getLoggedSuper()) - Case initTestCase = workflowService.createCase(petriNetService.getNewestVersionByIdentifier("new_init_test").stringId, "New init test", "", superCreator.getLoggedSuper()).getCase() + Case initTestCase = workflowService.createCase(petriNetService.getActiveVersionByIdentifier("new_init_test").stringId, "New init test", "", superCreator.getLoggedSuper()).getCase() assert (initTestCase.dataSet["new_init_multichoice"].value as List).stream().any { it.defaultValue == "Bob" } assert (initTestCase.dataSet["new_init_multichoice"].value as List).stream().any { it.defaultValue == "Alice" } assert (initTestCase.dataSet["old_init_multichoice"].value as List).stream().any { it.defaultValue == "Bob" } diff --git a/application-engine/src/test/resources/change_caseref_value_action_test.xml b/application-engine/src/test/resources/change_caseref_value_action_test.xml index cf908b29fe..ebaf019330 100644 --- a/application-engine/src/test/resources/change_caseref_value_action_test.xml +++ b/application-engine/src/test/resources/change_caseref_value_action_test.xml @@ -29,7 +29,7 @@ caseref: f.caseref; - def net = petriNetService.getNewestVersionByIdentifier("change_value"); + def net = petriNetService.getActiveVersionByIdentifier("change_value"); def newCase = workflowService.createCase(net.stringId, "", "", userService.transformToLoggedUser(userService.getLoggedOrSystem())).getACase() def newValue = new ArrayList(caseref.value) newValue.add(newCase) @@ -43,7 +43,7 @@ caseref: f.caseref; - def net = petriNetService.getNewestVersionByIdentifier("test"); + def net = petriNetService.getActiveVersionByIdentifier("test"); def newCase = workflowService.createCase(net.stringId, "", "", userService.transformToLoggedUser(userService.getLoggedOrSystem())).getACase() def newValue = new ArrayList(caseref.value) newValue.add(newCase) diff --git a/application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xml b/application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xml index 3379437305..c019e50ab2 100644 --- a/application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xml +++ b/application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xml @@ -217,7 +217,7 @@ export - def processId = petriNetService.getNewestVersionByIdentifier("export_test").stringId + def processId = petriNetService.getActiveVersionByIdentifier("export_test").stringId def config = new com.netgrif.application.engine.export.domain.ExportDataConfig() config.setDataToExport(["immediate_multichoice","immediate_number", "text"] as LinkedHashSet) exportTasksToFile({it.processId.eq(processId) & it.transitionId.eq("t3")},"src/test/resources/csv/task_mongo_export.csv", config) diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java index bdec2b24c7..04a8177dc9 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java @@ -24,6 +24,8 @@ public abstract class ElasticPetriNet { private Version version; + private boolean isVersionActive; + private String uriNodeId; private I18nField title; @@ -38,6 +40,7 @@ public ElasticPetriNet(PetriNet net) { this.id = net.getStringId(); this.identifier = net.getIdentifier(); this.version = net.getVersion(); + this.isVersionActive = net.isVersionActive(); this.uriNodeId = net.getUriNodeId(); this.title = this.transformToField(net.getTitle()); this.initials = net.getInitials(); @@ -46,6 +49,7 @@ public ElasticPetriNet(PetriNet net) { public void update(ElasticPetriNet net) { this.version = net.getVersion(); + this.isVersionActive = net.isVersionActive(); if (net.getUriNodeId() != null) { this.uriNodeId = net.getUriNodeId(); } diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java index a8b2fa447c..16e50f1385 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java @@ -1,5 +1,6 @@ package com.netgrif.application.engine.objects.petrinet.domain; +import com.netgrif.application.engine.objects.annotations.Indexed; import com.netgrif.application.engine.objects.auth.domain.ActorRef; import com.netgrif.application.engine.objects.petrinet.domain.arcs.Arc; import com.netgrif.application.engine.objects.petrinet.domain.arcs.reference.Referencable; @@ -30,7 +31,7 @@ public abstract class PetriNet extends PetriNetObject { @Getter @Setter - private String identifier; //combination of identifier and version must be unique ... maybe use @CompoundIndex? + private String identifier; // todo: combination of identifier and version must be unique ... maybe use @CompoundIndex? @Getter @Setter @@ -72,6 +73,11 @@ public abstract class PetriNet extends PetriNetObject { @Setter private Version version; + @Getter + @Setter + @Indexed + private boolean isVersionActive; + @Getter @Setter private ActorRef author; @@ -141,22 +147,23 @@ public PetriNet() { this.title = new I18nString(""); this.importId = ""; this.version = new Version(); - defaultCaseName = new I18nString(""); - initialized = false; - creationDate = LocalDateTime.now(); - places = new LinkedHashMap<>(); - transitions = new LinkedHashMap<>(); - arcs = new LinkedHashMap<>(); - dataSet = new LinkedHashMap<>(); - roles = new LinkedHashMap<>(); - negativeViewRoles = new LinkedList<>(); - transactions = new LinkedHashMap<>(); - processEvents = new LinkedHashMap<>(); - caseEvents = new LinkedHashMap<>(); - permissions = new HashMap<>(); - userRefs = new HashMap<>(); - functions = new LinkedList<>(); - tags = new HashMap<>(); + this.defaultCaseName = new I18nString(""); + this.initialized = false; + this.creationDate = LocalDateTime.now(); + this.places = new LinkedHashMap<>(); + this.transitions = new LinkedHashMap<>(); + this.arcs = new LinkedHashMap<>(); + this.dataSet = new LinkedHashMap<>(); + this.roles = new LinkedHashMap<>(); + this.negativeViewRoles = new LinkedList<>(); + this.transactions = new LinkedHashMap<>(); + this.processEvents = new LinkedHashMap<>(); + this.caseEvents = new LinkedHashMap<>(); + this.permissions = new HashMap<>(); + this.userRefs = new HashMap<>(); + this.functions = new LinkedList<>(); + this.tags = new HashMap<>(); + this.makeInactive(); } public PetriNet(PetriNet petriNet) { @@ -167,6 +174,7 @@ public PetriNet(PetriNet petriNet) { this.title = petriNet.getTitle(); this.importId = petriNet.getImportId(); this.version = petriNet.getVersion(); + this.isVersionActive = petriNet.isVersionActive(); this.defaultCaseName = petriNet.getDefaultCaseName(); this.defaultCaseNameExpression = petriNet.getDefaultCaseNameExpression(); this.initials = petriNet.getInitials(); @@ -430,6 +438,14 @@ public boolean hasDynamicCaseName() { return defaultCaseNameExpression != null; } + public void makeActive() { + this.setVersionActive(true); + } + + public void makeInactive() { + this.setVersionActive(false); + } + @Override public String getStringId() { return _id.toString(); diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/version/Version.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/version/Version.java index 3c5c2d84be..0592ab8e21 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/version/Version.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/version/Version.java @@ -52,6 +52,12 @@ public void increment(VersionType type) { } } + /** + * todo javadoc + * 0 equals + * <0 this < other + * >0 this > other + */ public int compareTo(Version other) { if (this.major != other.major) { return Long.compare(this.major, other.major); @@ -62,6 +68,20 @@ public int compareTo(Version other) { return Long.compare(this.patch, other.patch); } + /** + * todo javadoc + */ + public boolean isHigherThan(Version other) { + return compareTo(other) > 0; + } + + /** + * todo javadoc + */ + public boolean isLowerThan(Version other) { + return compareTo(other) < 0; + } + @Override public Version clone() { diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/domain/PetriNet.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/domain/PetriNet.java index 7e91f07c13..4076351e98 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/domain/PetriNet.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/domain/PetriNet.java @@ -60,6 +60,7 @@ public LinkedHashMap getRoles() { return super.getRoles(); } + // todo: delete clone method if not needed // @Override // public PetriNet clone() { // PetriNet clone = new PetriNet(); From b5bd5b52d12dd0e9d562d04960251eb3f597e7e4 Mon Sep 17 00:00:00 2001 From: chvostek Date: Wed, 19 Nov 2025 12:57:39 +0100 Subject: [PATCH 17/40] [NAE-2266] Introduce active version of process - fix repository query - fix null pointer exception - fix unwanted java reference --- .../petrinet/domain/repositories/PetriNetRepository.java | 2 +- .../engine/petrinet/service/PetriNetService.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java index d46394681b..ae445cbe9a 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java @@ -36,7 +36,7 @@ public interface PetriNetRepository extends MongoRepository, Q /** * todo javadoc */ - PetriNet findByIdentifierAndVersionActive(String identifier, boolean isVersionActive); + PetriNet findByIdentifierAndIsVersionActive(String identifier, boolean isVersionActive); /** * Finds a paginated list of {@link PetriNet} entities by their identifier. diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index 8dc3bcff8f..1f8b34d181 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -200,7 +200,7 @@ public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionTyp PetriNet newProcess = importedProcess.get(); PetriNet inactivatedProcess = null; - if (self.getPetriNet(newProcess.getIdentifier(), newProcess.getVersion()) != null) { + if (newProcess.getVersion() != null && self.getPetriNet(newProcess.getIdentifier(), newProcess.getVersion()) != null) { throw new IllegalArgumentException("A process [%s] with such version [%s] already exists" .formatted(newProcess.getIdentifier(), newProcess.getVersion())); } @@ -209,7 +209,7 @@ public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionTyp newProcess.setVersion(new Version()); } else { if (newProcess.getVersion() == null) { - newProcess.setVersion(existingLatestProcess.getVersion()); + newProcess.setVersion(existingLatestProcess.getVersion().clone()); newProcess.incrementVersion(releaseType); } else if (newProcess.getVersion().isLowerThan(existingLatestProcess.getVersion())) { throw new IllegalArgumentException("Only higher versions of process [%s] are allowed to import. The requested version [%s] cannot be imported because the higher version [%s] already exists." @@ -322,7 +322,7 @@ public PetriNet getActiveVersionByIdentifier(String identifier) { if (identifier == null) { return null; } - return repository.findByIdentifierAndVersionActive(identifier, true); + return repository.findByIdentifierAndIsVersionActive(identifier, true); } @Override From 9059beca3ba54da2a6c102e606e1e719eaf7d20e Mon Sep 17 00:00:00 2001 From: chvostek Date: Wed, 19 Nov 2025 13:22:06 +0100 Subject: [PATCH 18/40] [NAE-2266] Introduce active version of process - fix version initialization --- .../engine/petrinet/service/PetriNetService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index 1f8b34d181..67cac75c20 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -205,17 +205,17 @@ public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionTyp .formatted(newProcess.getIdentifier(), newProcess.getVersion())); } PetriNet existingLatestProcess = self.getLatestVersionByIdentifier(newProcess.getIdentifier()); - if (existingLatestProcess == null) { + if (existingLatestProcess == null && newProcess.getVersion() == null) { newProcess.setVersion(new Version()); } else { if (newProcess.getVersion() == null) { newProcess.setVersion(existingLatestProcess.getVersion().clone()); newProcess.incrementVersion(releaseType); - } else if (newProcess.getVersion().isLowerThan(existingLatestProcess.getVersion())) { + } else if (existingLatestProcess != null && newProcess.getVersion().isLowerThan(existingLatestProcess.getVersion())) { throw new IllegalArgumentException("Only higher versions of process [%s] are allowed to import. The requested version [%s] cannot be imported because the higher version [%s] already exists." .formatted(newProcess.getIdentifier(), newProcess.getVersion(), existingLatestProcess.getVersion())); } - if (existingLatestProcess.isVersionActive()) { + if (existingLatestProcess != null && existingLatestProcess.isVersionActive()) { existingLatestProcess.makeInactive(); inactivatedProcess = existingLatestProcess; } else { From dfd7afd4863a8e8e71afaefa9aa62189efb0eff6 Mon Sep 17 00:00:00 2001 From: chvostek Date: Thu, 20 Nov 2025 10:17:22 +0100 Subject: [PATCH 19/40] [NAE-2266] Introduce active version of process - implement tests to test the version --- .../service/PetriNetServiceTest.groovy | 96 ++++++++++++++++--- .../petriNets/process_version_1_0_0.xml | 9 ++ .../petriNets/process_version_2_0_0.xml | 9 ++ .../petriNets/process_version_3_0_0.xml | 9 ++ .../petriNets/process_version_4_0_0.xml | 9 ++ .../petriNets/process_version_5_0_0.xml | 9 ++ 6 files changed, 126 insertions(+), 15 deletions(-) create mode 100644 application-engine/src/test/resources/petriNets/process_version_1_0_0.xml create mode 100644 application-engine/src/test/resources/petriNets/process_version_2_0_0.xml create mode 100644 application-engine/src/test/resources/petriNets/process_version_3_0_0.xml create mode 100644 application-engine/src/test/resources/petriNets/process_version_4_0_0.xml create mode 100644 application-engine/src/test/resources/petriNets/process_version_5_0_0.xml diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy index 84ce8b5ee0..c540bafcd7 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy @@ -1,29 +1,24 @@ package com.netgrif.application.engine.petrinet.service import com.netgrif.application.engine.TestHelper -import com.netgrif.application.engine.objects.auth.domain.ActorRef -import com.netgrif.application.engine.objects.auth.domain.ActorTransformer -import com.netgrif.application.engine.objects.auth.domain.Authority -import com.netgrif.application.engine.objects.auth.domain.User -import com.netgrif.application.engine.objects.auth.domain.enums.UserState +import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService import com.netgrif.application.engine.auth.service.UserService -import com.netgrif.application.engine.objects.elastic.domain.ElasticPetriNet import com.netgrif.application.engine.elastic.domain.ElasticPetriNetRepository import com.netgrif.application.engine.ipc.TaskApiTest +import com.netgrif.application.engine.objects.auth.domain.* +import com.netgrif.application.engine.objects.auth.domain.enums.UserState +import com.netgrif.application.engine.objects.elastic.domain.ElasticPetriNet import com.netgrif.application.engine.objects.petrinet.domain.PetriNet import com.netgrif.application.engine.objects.petrinet.domain.PetriNetSearch -import com.netgrif.application.engine.objects.petrinet.domain.UriContentType -import com.netgrif.application.engine.objects.petrinet.domain.UriNode import com.netgrif.application.engine.objects.petrinet.domain.VersionType -import com.netgrif.application.engine.petrinet.domain.repositories.PetriNetRepository import com.netgrif.application.engine.objects.petrinet.domain.roles.ProcessRole -import com.netgrif.application.engine.petrinet.domain.roles.ProcessRoleRepository import com.netgrif.application.engine.objects.petrinet.domain.version.Version +import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.petrinetoutcomes.ImportPetriNetEventOutcome +import com.netgrif.application.engine.petrinet.domain.repositories.PetriNetRepository +import com.netgrif.application.engine.petrinet.domain.roles.ProcessRoleRepository import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService -import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService import com.netgrif.application.engine.startup.ImportHelper import com.netgrif.application.engine.startup.runner.SuperCreatorRunner -import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.petrinetoutcomes.ImportPetriNetEventOutcome import com.netgrif.application.engine.workflow.domain.repositories.CaseRepository import com.netgrif.application.engine.workflow.domain.repositories.TaskRepository import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService @@ -39,6 +34,8 @@ import org.springframework.data.domain.PageRequest import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.junit.jupiter.SpringExtension +import static org.junit.jupiter.api.Assertions.assertThrows + @Disabled @ExtendWith(SpringExtension.class) @ActiveProfiles(["test"]) @@ -46,6 +43,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension class PetriNetServiceTest { public static final String NET_FILE = "process_delete_test.xml" + public static final String VERSION_PROCESS_FILE_FORMAT = "petriNets/process_version_%s_0_0.xml" public static final String NET_SEARCH_FILE = "process_search_test.xml" public static final String CUSTOMER_USER_MAIL = "customer@netgrif.com" @@ -106,7 +104,7 @@ class PetriNetServiceTest { long taskCount = taskRepository.count() - ImportPetriNetEventOutcome testNetOptional = petriNetService.importPetriNet(stream(NET_FILE), VersionType.MAJOR, superCreator.getLoggedSuper()) + ImportPetriNetEventOutcome testNetOptional = importProcess(NET_FILE, superCreator.getLoggedSuper()) assert testNetOptional.getNet() != null assert petriNetRepository.count() == processCount + 1 PetriNet testNet = testNetOptional.getNet() @@ -149,6 +147,70 @@ class PetriNetServiceTest { assert exceptionThrown } + @Test + void testVersionsOnImport() { + ImportPetriNetEventOutcome outcome = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("2"), superCreator.loggedSuper) + PetriNet petriNetV2 = outcome.getNet() + assert petriNetV2 != null + assert petriNetV2.isVersionActive() + Version version = new Version() + version.setMajor(2) + assert petriNetV2.getVersion() == version + Thread.sleep(5000) + Optional elasticPetriNetV2Optional = elasticPetriNetRepository.findById(petriNetV2.stringId) + assert elasticPetriNetV2Optional.isPresent() + assert elasticPetriNetV2Optional.get().isVersionActive() + + outcome = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("4"), superCreator.loggedSuper) + PetriNet petriNetV4 = outcome.getNet() + assert petriNetV4 != null + assert !petriNetService.get(petriNetV2.getObjectId()).isVersionActive() + assert petriNetV4.isVersionActive() + version = new Version() + version.setMajor(4) + assert petriNetV4.getVersion() == version + Thread.sleep(5000) + elasticPetriNetV2Optional = elasticPetriNetRepository.findById(petriNetV2.stringId) + assert !elasticPetriNetV2Optional.get().isVersionActive() + Optional elasticPetriNetV4Optional = elasticPetriNetRepository.findById(petriNetV4.stringId) + assert elasticPetriNetV4Optional.isPresent() + assert elasticPetriNetV4Optional.get().isVersionActive() + + assertThrows(IllegalArgumentException.class, { + // cannot import with lower version number than the highest + importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("1"), superCreator.loggedSuper) + }) + assertThrows(IllegalArgumentException.class, { + // cannot import already existing version + importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("2"), superCreator.loggedSuper) + }) + + petriNetV2.makeActive() + petriNetV2 = petriNetService.save(petriNetV2).get() + assert petriNetV2.isVersionActive() + petriNetV4.makeInactive() + petriNetV4 = petriNetService.save(petriNetV4).get() + assert !petriNetV4.isVersionActive() + + outcome = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("5"), superCreator.loggedSuper) + PetriNet petriNetV5 = outcome.getNet() + assert petriNetV5 != null + assert !petriNetService.get(petriNetV2.getObjectId()).isVersionActive() + assert !petriNetService.get(petriNetV4.getObjectId()).isVersionActive() + assert petriNetV5.isVersionActive() + version = new Version() + version.setMajor(5) + assert petriNetV5.getVersion() == version + Thread.sleep(5000) + elasticPetriNetV2Optional = elasticPetriNetRepository.findById(petriNetV2.stringId) + assert !elasticPetriNetV2Optional.get().isVersionActive() + elasticPetriNetV4Optional = elasticPetriNetRepository.findById(petriNetV4.stringId) + assert !elasticPetriNetV4Optional.get().isVersionActive() + Optional elasticPetriNetV5Optional = elasticPetriNetRepository.findById(petriNetV5.stringId) + assert elasticPetriNetV5Optional.isPresent() + assert elasticPetriNetV5Optional.get().isVersionActive() + } + @Test void processSearch() { long processCount = petriNetRepository.count() @@ -156,8 +218,8 @@ class PetriNetServiceTest { def user = userService.findUserByUsername(CUSTOMER_USER_MAIL, null) assert user != null && user.isPresent() - petriNetService.importPetriNet(stream(NET_FILE), VersionType.MAJOR, superCreator.getLoggedSuper()) - petriNetService.importPetriNet(stream(NET_SEARCH_FILE), VersionType.MAJOR, ActorTransformer.toLoggedUser(user.get())) + importProcess(NET_FILE, superCreator.getLoggedSuper()) + importProcess(NET_SEARCH_FILE, ActorTransformer.toLoggedUser(user.get())) assert petriNetRepository.count() == processCount + 2 @@ -209,4 +271,8 @@ class PetriNetServiceTest { search8.setAuthor(author); assert petriNetService.search(search8, superCreator.getLoggedSuper(), PageRequest.of(0, 50), LocaleContextHolder.locale).getNumberOfElements() == 1; } + + private ImportPetriNetEventOutcome importProcess(String filePath, LoggedUser author) { + return petriNetService.importPetriNet(stream(filePath), VersionType.MAJOR, author) + } } diff --git a/application-engine/src/test/resources/petriNets/process_version_1_0_0.xml b/application-engine/src/test/resources/petriNets/process_version_1_0_0.xml new file mode 100644 index 0000000000..6359404689 --- /dev/null +++ b/application-engine/src/test/resources/petriNets/process_version_1_0_0.xml @@ -0,0 +1,9 @@ + + + process_version_test + 1.0.0 + PVT + Process Version 1.0.0 test + false + diff --git a/application-engine/src/test/resources/petriNets/process_version_2_0_0.xml b/application-engine/src/test/resources/petriNets/process_version_2_0_0.xml new file mode 100644 index 0000000000..f095c407ae --- /dev/null +++ b/application-engine/src/test/resources/petriNets/process_version_2_0_0.xml @@ -0,0 +1,9 @@ + + + process_version_test + 2.0.0 + PVT + Process Version 2.0.0 test + false + diff --git a/application-engine/src/test/resources/petriNets/process_version_3_0_0.xml b/application-engine/src/test/resources/petriNets/process_version_3_0_0.xml new file mode 100644 index 0000000000..fdf75f4294 --- /dev/null +++ b/application-engine/src/test/resources/petriNets/process_version_3_0_0.xml @@ -0,0 +1,9 @@ + + + process_version_test + 3.0.0 + PVT + Process Version 3.0.0 test + false + diff --git a/application-engine/src/test/resources/petriNets/process_version_4_0_0.xml b/application-engine/src/test/resources/petriNets/process_version_4_0_0.xml new file mode 100644 index 0000000000..58e472c139 --- /dev/null +++ b/application-engine/src/test/resources/petriNets/process_version_4_0_0.xml @@ -0,0 +1,9 @@ + + + process_version_test + 4.0.0 + PVT + Process Version 4.0.0 test + false + diff --git a/application-engine/src/test/resources/petriNets/process_version_5_0_0.xml b/application-engine/src/test/resources/petriNets/process_version_5_0_0.xml new file mode 100644 index 0000000000..21b309f9be --- /dev/null +++ b/application-engine/src/test/resources/petriNets/process_version_5_0_0.xml @@ -0,0 +1,9 @@ + + + process_version_test + 5.0.0 + PVT + Process Version 5.0.0 test + false + From e8441cb517bfeada2e070f65c907b2277b1e7daf Mon Sep 17 00:00:00 2001 From: chvostek Date: Thu, 20 Nov 2025 11:49:55 +0100 Subject: [PATCH 20/40] [NAE-2266] Introduce active version of process - implement API to activate the process --- .../petrinet/service/PetriNetService.java | 41 ++++++++++++++++++- .../service/interfaces/IPetriNetService.java | 8 +++- .../petrinet/web/PetriNetController.java | 25 +++++++++++ .../petrinet/MakeVersionActiveDTO.java | 10 +++++ 4 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/response/petrinet/MakeVersionActiveDTO.java diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index 67cac75c20..bbf03beb12 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -5,6 +5,7 @@ import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import com.netgrif.application.engine.configuration.properties.CacheConfigurationProperties; import com.netgrif.application.engine.files.minio.StorageConfigurationProperties; +import com.netgrif.application.engine.objects.dto.response.petrinet.MakeVersionActiveDTO; import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; import com.netgrif.application.engine.petrinet.web.responsebodies.ArcImportReference; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; @@ -245,7 +246,7 @@ public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionTyp } catch (Exception rethrow) { if (inactivatedProcess != null) { // make sure there is always an active version - log.warn("Something unexpected happened while saving new version of process [{}]. Rolling back status of a version, that have been inactivated...", + log.warn("Something unexpected happened while saving new version of process [{}]. Rolling back process version, that have been inactivated...", newProcess.getIdentifier()); inactivatedProcess.makeActive(); save(inactivatedProcess); @@ -258,6 +259,44 @@ public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionTyp return outcome; } + @Override + public MakeVersionActiveDTO makeVersionActive(String processId) { + log.info("Activating the process with id [{}]...", processId); + PetriNet processToActivate = self.getPetriNet(processId); + PetriNet processToInactivate = self.getActiveVersionByIdentifier(processToActivate.getIdentifier()); + + if (processToInactivate != null && processToInactivate.getStringId().equals(processToActivate.getStringId())) { + log.debug("The process to activate is already active. Nothing to do"); + return new MakeVersionActiveDTO(); + } + + if (processToInactivate != null) { + log.debug("Inactivating current active version of process identifier and ID [{}][{}]", + processToInactivate.getIdentifier(), processToInactivate.getStringId()); + processToInactivate.makeInactive(); + save(processToInactivate); + } + + try { + processToActivate.makeActive(); + save(processToActivate); + } catch (Exception rethrow) { + if (processToInactivate != null) { + // make sure there is always an active version + log.warn("Something unexpected happened while activating process [{}]. Rolling back status of a version, that have been inactivated...", + processId); + processToInactivate.makeActive(); + save(processToInactivate); + } + throw rethrow; + } + + log.debug("Successfully activated process with ID [{}] of identifier [{}]", processToActivate.getIdentifier(), + processToActivate.getStringId()); + return new MakeVersionActiveDTO(processToActivate.getStringId(), processToInactivate == null ? null + : processToInactivate.getStringId()); + } + protected void evaluateRules(Event event) { publisher.publishEvent(event); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java index 8988afd566..245b62bacd 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java @@ -1,6 +1,7 @@ package com.netgrif.application.engine.petrinet.service.interfaces; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; +import com.netgrif.application.engine.objects.dto.response.petrinet.MakeVersionActiveDTO; import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; import com.netgrif.application.engine.objects.petrinet.domain.PetriNetSearch; import com.netgrif.application.engine.objects.petrinet.domain.Transition; @@ -110,6 +111,11 @@ static DataFieldReference transformToReference(PetriNet net, Transition transiti */ ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionType releaseType, LoggedUser user, Map params) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException; + /** + * todo javadoc + */ + MakeVersionActiveDTO makeVersionActive(String processId); + /** * Saves a PetriNet object. * @@ -157,7 +163,7 @@ static DataFieldReference transformToReference(PetriNet net, Transition transiti * todo javadoc isversionactive logic * * @param identifier the unique identifier of the PetriNet - * @return the active version of the {@link PetriNet} matching the provided identifier + * @return the active version of the {@link PetriNet} matching the provided identifier or null if not found */ PetriNet getActiveVersionByIdentifier(String identifier); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java index eb9cb4f90f..aa001ebed1 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java @@ -6,6 +6,7 @@ import com.netgrif.application.engine.eventoutcomes.LocalisedEventOutcomeFactory; import com.netgrif.application.engine.importer.service.Importer; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; +import com.netgrif.application.engine.objects.dto.response.petrinet.MakeVersionActiveDTO; import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; import com.netgrif.application.engine.objects.petrinet.domain.PetriNetSearch; import com.netgrif.application.engine.objects.petrinet.domain.VersionType; @@ -39,6 +40,7 @@ import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.PagedModel; import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; @@ -120,6 +122,29 @@ public EntityModel importPetriNet( } } + @PreAuthorize("@authorizationService.hasAuthority('ADMIN')") + @Operation(summary = "Make the process active", + description = "Caller must have the ADMIN role. Makes the provided process active. The previous active version is inactivated.", + security = {@SecurityRequirement(name = "BasicAuth")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "OK. The response contains the IDs of process models, that have been activated and inactivated"), + @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), + @ApiResponse(responseCode = "404", description = "Process model is not found") + }) + @PostMapping(value = "/activate/{id}", produces = MediaTypes.HAL_JSON_VALUE) + public ResponseEntity makeVersionActive(@PathVariable("id") String processId) { + try { + MakeVersionActiveDTO response = service.makeVersionActive(processId); + return ResponseEntity.ok(response); + } catch (IllegalArgumentException e) { + log.error("The process with id [{}] was not found.", processId, e); + return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); + } catch (Exception e) { + log.error("Making process [{}] active failed: ", processId, e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } + @Operation(summary = "Get all processes", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(produces = MediaTypes.HAL_JSON_VALUE) public ResponseEntity> getAll(@RequestParam(value = "indentifier", required = false) String identifier, @RequestParam(value = "version", required = false) String version, Pageable pageable, Authentication auth, Locale locale) { diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/response/petrinet/MakeVersionActiveDTO.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/response/petrinet/MakeVersionActiveDTO.java new file mode 100644 index 0000000000..d4b414ab38 --- /dev/null +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/response/petrinet/MakeVersionActiveDTO.java @@ -0,0 +1,10 @@ +package com.netgrif.application.engine.objects.dto.response.petrinet; + +import java.io.Serializable; + +public record MakeVersionActiveDTO(String activatedProcessId, String inactivatedProcessId) implements Serializable { + + public MakeVersionActiveDTO() { + this(null, null); + } +} From 0c1c914224667c42cb01852dc862a6ee9ac0e91b Mon Sep 17 00:00:00 2001 From: chvostek Date: Thu, 20 Nov 2025 14:35:08 +0100 Subject: [PATCH 21/40] [NAE-2266] Introduce active version of process - fix logging --- .../engine/petrinet/service/PetriNetService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index bbf03beb12..c1c65d98d6 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -271,8 +271,8 @@ public MakeVersionActiveDTO makeVersionActive(String processId) { } if (processToInactivate != null) { - log.debug("Inactivating current active version of process identifier and ID [{}][{}]", - processToInactivate.getIdentifier(), processToInactivate.getStringId()); + log.debug("Inactivating current active version of process with ID [{}] of identifier [{}]", + processToInactivate.getStringId(), processToInactivate.getIdentifier()); processToInactivate.makeInactive(); save(processToInactivate); } @@ -291,8 +291,8 @@ public MakeVersionActiveDTO makeVersionActive(String processId) { throw rethrow; } - log.debug("Successfully activated process with ID [{}] of identifier [{}]", processToActivate.getIdentifier(), - processToActivate.getStringId()); + log.debug("Successfully activated process with ID [{}] of identifier [{}]", processToActivate.getStringId(), + processToActivate.getIdentifier()); return new MakeVersionActiveDTO(processToActivate.getStringId(), processToInactivate == null ? null : processToInactivate.getStringId()); } From 53fb4e63c8324123df3a1f5d8259db82bb873c2d Mon Sep 17 00:00:00 2001 From: chvostek Date: Fri, 21 Nov 2025 09:02:36 +0100 Subject: [PATCH 22/40] [NAE-2266] Introduce active version of process - fix log message --- .../application/engine/petrinet/service/PetriNetService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index c1c65d98d6..7b32c34029 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -234,7 +234,7 @@ public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionTyp functionCacheService.cachePetriNetFunctions(newProcess); Path savedPath = getImporter().saveNetFile(newProcess, new ByteArrayInputStream(xmlCopy.toByteArray())); xmlCopy.close(); - log.info("Petri newProcess " + newProcess.getTitle() + " (" + newProcess.getInitials() + " v" + newProcess.getVersion() + ") importedProcess successfully and saved in a folder: " + savedPath.toString()); + log.info("Petri net " + newProcess.getTitle() + " (" + newProcess.getInitials() + " v" + newProcess.getVersion() + ") imported successfully and saved in a folder: " + savedPath.toString()); outcome.setOutcomes(eventService.runActions(newProcess.getPreUploadActions(), null, Optional.empty(), params)); publisher.publishEvent(new ProcessDeployEvent(outcome, EventPhase.PRE)); From 1cdfbc484dd486691e3af466847b8b9178cdefb4 Mon Sep 17 00:00:00 2001 From: chvostek Date: Fri, 21 Nov 2025 10:37:39 +0100 Subject: [PATCH 23/40] [NAE-2266] Introduce active version of process - remove unused PetriNetService.evaluateRules method - remove the explicit rollback when saving process changes -> resolved in EE as transactions --- .../petrinet/service/PetriNetService.java | 38 +++---------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index 7b32c34029..3309755ae0 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -12,7 +12,6 @@ import com.netgrif.application.engine.auth.service.UserService; import com.netgrif.application.engine.elastic.service.interfaces.IElasticPetriNetMappingService; import com.netgrif.application.engine.elastic.service.interfaces.IElasticPetriNetService; -import com.netgrif.application.engine.objects.event.events.Event; import com.netgrif.application.engine.objects.event.events.petrinet.ProcessDeleteEvent; import com.netgrif.application.engine.objects.event.events.petrinet.ProcessDeployEvent; import com.netgrif.application.engine.importer.service.Importer; @@ -238,21 +237,12 @@ public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionTyp outcome.setOutcomes(eventService.runActions(newProcess.getPreUploadActions(), null, Optional.empty(), params)); publisher.publishEvent(new ProcessDeployEvent(outcome, EventPhase.PRE)); + if (inactivatedProcess != null) { save(inactivatedProcess); } - try { - save(newProcess); - } catch (Exception rethrow) { - if (inactivatedProcess != null) { - // make sure there is always an active version - log.warn("Something unexpected happened while saving new version of process [{}]. Rolling back process version, that have been inactivated...", - newProcess.getIdentifier()); - inactivatedProcess.makeActive(); - save(inactivatedProcess); - } - throw rethrow; - } + save(newProcess); + outcome.setOutcomes(eventService.runActions(newProcess.getPostUploadActions(), null, Optional.empty(), params)); outcome.setNet(importedProcess.get()); publisher.publishEvent(new ProcessDeployEvent(outcome, EventPhase.POST)); @@ -261,6 +251,7 @@ public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionTyp @Override public MakeVersionActiveDTO makeVersionActive(String processId) { + // todo 2266 test log.info("Activating the process with id [{}]...", processId); PetriNet processToActivate = self.getPetriNet(processId); PetriNet processToInactivate = self.getActiveVersionByIdentifier(processToActivate.getIdentifier()); @@ -276,20 +267,8 @@ public MakeVersionActiveDTO makeVersionActive(String processId) { processToInactivate.makeInactive(); save(processToInactivate); } - - try { - processToActivate.makeActive(); - save(processToActivate); - } catch (Exception rethrow) { - if (processToInactivate != null) { - // make sure there is always an active version - log.warn("Something unexpected happened while activating process [{}]. Rolling back status of a version, that have been inactivated...", - processId); - processToInactivate.makeActive(); - save(processToInactivate); - } - throw rethrow; - } + processToActivate.makeActive(); + save(processToActivate); log.debug("Successfully activated process with ID [{}] of identifier [{}]", processToActivate.getStringId(), processToActivate.getIdentifier()); @@ -297,11 +276,6 @@ public MakeVersionActiveDTO makeVersionActive(String processId) { : processToInactivate.getStringId()); } - protected void evaluateRules(Event event) { - publisher.publishEvent(event); - - } - @Override public Optional save(PetriNet petriNet) { petriNet.initializeArcs(); From 47ba662d8fce5c8221240e63b9348ba4db68c57a Mon Sep 17 00:00:00 2001 From: chvostek Date: Fri, 21 Nov 2025 11:24:50 +0100 Subject: [PATCH 24/40] [NAE-2266] Introduce active version of process - remove todos - implement PetriNetServiceTest.testMakeVersionActive --- .../petrinet/service/PetriNetService.java | 3 -- .../service/PetriNetServiceTest.groovy | 32 ++++++++++++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index 3309755ae0..c75b3aef9e 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -251,7 +251,6 @@ public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionTyp @Override public MakeVersionActiveDTO makeVersionActive(String processId) { - // todo 2266 test log.info("Activating the process with id [{}]...", processId); PetriNet processToActivate = self.getPetriNet(processId); PetriNet processToInactivate = self.getActiveVersionByIdentifier(processToActivate.getIdentifier()); @@ -331,7 +330,6 @@ public List findAllById(List ids) { @Override @Cacheable(value = "petriNetActive", unless = "#result == null") public PetriNet getActiveVersionByIdentifier(String identifier) { - // todo 2266 test if (identifier == null) { return null; } @@ -341,7 +339,6 @@ public PetriNet getActiveVersionByIdentifier(String identifier) { @Override @Cacheable(value = "petriNetLatest", unless = "#result == null") public PetriNet getLatestVersionByIdentifier(String identifier) { - // todo 2266 test if (identifier == null) { return null; } diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy index c540bafcd7..5755494e44 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy @@ -7,6 +7,7 @@ import com.netgrif.application.engine.elastic.domain.ElasticPetriNetRepository import com.netgrif.application.engine.ipc.TaskApiTest import com.netgrif.application.engine.objects.auth.domain.* import com.netgrif.application.engine.objects.auth.domain.enums.UserState +import com.netgrif.application.engine.objects.dto.response.petrinet.MakeVersionActiveDTO import com.netgrif.application.engine.objects.elastic.domain.ElasticPetriNet import com.netgrif.application.engine.objects.petrinet.domain.PetriNet import com.netgrif.application.engine.objects.petrinet.domain.PetriNetSearch @@ -211,11 +212,40 @@ class PetriNetServiceTest { assert elasticPetriNetV5Optional.get().isVersionActive() } + @Test + void testMakeVersionActive() { + PetriNet petriNetV1 = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("1"), superCreator.loggedSuper).getNet() + PetriNet petriNetV2 = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("2"), superCreator.loggedSuper).getNet() + PetriNet petriNetV3 = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("3"), superCreator.loggedSuper).getNet() + assert !petriNetService.get(petriNetV1.getObjectId()).isVersionActive() + assert !petriNetService.get(petriNetV2.getObjectId()).isVersionActive() + assert petriNetService.get(petriNetV3.getObjectId()).isVersionActive() + + MakeVersionActiveDTO response = petriNetService.makeVersionActive(petriNetV2.getStringId()) + assert response != null + assert response.activatedProcessId() == petriNetV2.getStringId() + assert response.inactivatedProcessId() == petriNetV3.getStringId() + petriNetV2 = petriNetService.get(petriNetV2.getObjectId()) + assert !petriNetService.get(petriNetV1.getObjectId()).isVersionActive() + assert petriNetService.get(petriNetV2.getObjectId()).isVersionActive() + assert !petriNetService.get(petriNetV3.getObjectId()).isVersionActive() + + petriNetV2.makeInactive() + petriNetService.save(petriNetV2) + + response = petriNetService.makeVersionActive(petriNetV3.getStringId()) + assert response != null + assert response.activatedProcessId() == petriNetV3.getStringId() + assert response.inactivatedProcessId() == null + assert !petriNetService.get(petriNetV1.getObjectId()).isVersionActive() + assert !petriNetService.get(petriNetV2.getObjectId()).isVersionActive() + assert petriNetService.get(petriNetV3.getObjectId()).isVersionActive() + } + @Test void processSearch() { long processCount = petriNetRepository.count() - def user = userService.findUserByUsername(CUSTOMER_USER_MAIL, null) assert user != null && user.isPresent() importProcess(NET_FILE, superCreator.getLoggedSuper()) From 7e0c9b8910e7de4e672c2ef72389a38f69663de4 Mon Sep 17 00:00:00 2001 From: chvostek Date: Fri, 21 Nov 2025 13:55:52 +0100 Subject: [PATCH 25/40] [NAE-2266] Introduce active version of process - fix tests --- .../application/engine/petrinet/domain/PetriNetTest.groovy | 2 +- .../engine/petrinet/service/CachePetriNetServiceTest.groovy | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy index 19b9b76ec8..f0e5464685 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy @@ -136,7 +136,7 @@ class PetriNetTest { try { petriNetService.importPetriNet(netResource3.inputStream, VersionType.MAJOR, superCreator.loggedSuper) } catch (Exception e) { - assert e.getMessage() == "Provided Petri net version is already present in the system" + assert e.getMessage() == "A process [test] with such version [0.0.1] already exists" } } diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy index 2f52400152..a222b227b0 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy @@ -67,13 +67,13 @@ class CachePetriNetServiceTest { @Test void cacheTest() { - assert cacheManager.getCache(cacheProperties.getPetriNetNewest()).get("processDeleteTest") == null + assert cacheManager.getCache(cacheProperties.getPetriNetActive()).get("processDeleteTest") == null ImportPetriNetEventOutcome testNetOptional = petriNetService.importPetriNet(stream(NET_FILE), VersionType.MAJOR, superCreator.getLoggedSuper()) assert testNetOptional.getNet() != null PetriNet testNet = testNetOptional.getNet() - assert cacheManager.getCache(cacheProperties.getPetriNetNewest()).get(testNet.getIdentifier()) == null + assert cacheManager.getCache(cacheProperties.getPetriNetActive()).get(testNet.getIdentifier()) == null PetriNet test = petriNetService.getActiveVersionByIdentifier(testNet.getIdentifier()) - assert cacheManager.getCache(cacheProperties.getPetriNetNewest()).get(testNet.getIdentifier()).get().equals(test) + assert cacheManager.getCache(cacheProperties.getPetriNetActive()).get(testNet.getIdentifier()).get().equals(test) } } From 4670f33fb3b222442a484f011a910611cc223a84 Mon Sep 17 00:00:00 2001 From: chvostek Date: Fri, 21 Nov 2025 15:11:25 +0100 Subject: [PATCH 26/40] [NAE-2266] Introduce active version of process - split PetriNetService.save into two methods --- .../engine/petrinet/service/PetriNetService.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index c75b3aef9e..befb673bb2 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -239,9 +239,9 @@ public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionTyp publisher.publishEvent(new ProcessDeployEvent(outcome, EventPhase.PRE)); if (inactivatedProcess != null) { - save(inactivatedProcess); + doSaveInternal(inactivatedProcess); } - save(newProcess); + doSaveInternal(newProcess); outcome.setOutcomes(eventService.runActions(newProcess.getPostUploadActions(), null, Optional.empty(), params)); outcome.setNet(importedProcess.get()); @@ -264,10 +264,10 @@ public MakeVersionActiveDTO makeVersionActive(String processId) { log.debug("Inactivating current active version of process with ID [{}] of identifier [{}]", processToInactivate.getStringId(), processToInactivate.getIdentifier()); processToInactivate.makeInactive(); - save(processToInactivate); + doSaveInternal(processToInactivate); } processToActivate.makeActive(); - save(processToActivate); + doSaveInternal(processToActivate); log.debug("Successfully activated process with ID [{}] of identifier [{}]", processToActivate.getStringId(), processToActivate.getIdentifier()); @@ -277,6 +277,10 @@ public MakeVersionActiveDTO makeVersionActive(String processId) { @Override public Optional save(PetriNet petriNet) { + return doSaveInternal(petriNet); + } + + protected final Optional doSaveInternal(PetriNet petriNet) { petriNet.initializeArcs(); this.evictCache(petriNet); petriNet = repository.save(petriNet); @@ -284,7 +288,7 @@ public Optional save(PetriNet petriNet) { try { elasticPetriNetService.indexNow(this.petriNetMappingService.transform(petriNet)); } catch (Exception e) { - log.error("Indexing failed [" + petriNet.getStringId() + "]", e); + log.error("Indexing failed [{}]", petriNet.getStringId(), e); } return Optional.of(petriNet); From cf0651a2d8e3ea90fd38a1f67d63fa5d82888071 Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 24 Nov 2025 10:31:10 +0100 Subject: [PATCH 27/40] [NAE-2266] Introduce active version of process - rename version active attribute in PetriNet --- .../repositories/PetriNetRepository.java | 2 +- .../petrinet/service/PetriNetService.java | 2 +- .../service/PetriNetServiceTest.groovy | 36 +++++++++---------- .../elastic/domain/ElasticPetriNet.java | 6 ++-- .../objects/petrinet/domain/PetriNet.java | 4 +-- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java index ae445cbe9a..bb1d1587ec 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java @@ -36,7 +36,7 @@ public interface PetriNetRepository extends MongoRepository, Q /** * todo javadoc */ - PetriNet findByIdentifierAndIsVersionActive(String identifier, boolean isVersionActive); + PetriNet findByIdentifierAndVersionActive(String identifier, boolean versionActive); /** * Finds a paginated list of {@link PetriNet} entities by their identifier. diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index befb673bb2..cf4326a483 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -337,7 +337,7 @@ public PetriNet getActiveVersionByIdentifier(String identifier) { if (identifier == null) { return null; } - return repository.findByIdentifierAndIsVersionActive(identifier, true); + return repository.findByIdentifierAndVersionActive(identifier, true); } @Override diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy index 5755494e44..d283ffdc13 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy @@ -153,7 +153,7 @@ class PetriNetServiceTest { ImportPetriNetEventOutcome outcome = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("2"), superCreator.loggedSuper) PetriNet petriNetV2 = outcome.getNet() assert petriNetV2 != null - assert petriNetV2.isVersionActive() + assert petriNetV2.versionActive() Version version = new Version() version.setMajor(2) assert petriNetV2.getVersion() == version @@ -188,28 +188,28 @@ class PetriNetServiceTest { petriNetV2.makeActive() petriNetV2 = petriNetService.save(petriNetV2).get() - assert petriNetV2.isVersionActive() + assert petriNetV2.versionActive() petriNetV4.makeInactive() petriNetV4 = petriNetService.save(petriNetV4).get() - assert !petriNetV4.isVersionActive() + assert !petriNetV4.versionActive() outcome = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("5"), superCreator.loggedSuper) PetriNet petriNetV5 = outcome.getNet() assert petriNetV5 != null - assert !petriNetService.get(petriNetV2.getObjectId()).isVersionActive() - assert !petriNetService.get(petriNetV4.getObjectId()).isVersionActive() - assert petriNetV5.isVersionActive() + assert !petriNetService.get(petriNetV2.getObjectId()).versionActive() + assert !petriNetService.get(petriNetV4.getObjectId()).versionActive() + assert petriNetV5.versionActive() version = new Version() version.setMajor(5) assert petriNetV5.getVersion() == version Thread.sleep(5000) elasticPetriNetV2Optional = elasticPetriNetRepository.findById(petriNetV2.stringId) - assert !elasticPetriNetV2Optional.get().isVersionActive() + assert !elasticPetriNetV2Optional.get().versionActive() elasticPetriNetV4Optional = elasticPetriNetRepository.findById(petriNetV4.stringId) - assert !elasticPetriNetV4Optional.get().isVersionActive() + assert !elasticPetriNetV4Optional.get().versionActive() Optional elasticPetriNetV5Optional = elasticPetriNetRepository.findById(petriNetV5.stringId) assert elasticPetriNetV5Optional.isPresent() - assert elasticPetriNetV5Optional.get().isVersionActive() + assert elasticPetriNetV5Optional.get().versionActive() } @Test @@ -217,18 +217,18 @@ class PetriNetServiceTest { PetriNet petriNetV1 = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("1"), superCreator.loggedSuper).getNet() PetriNet petriNetV2 = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("2"), superCreator.loggedSuper).getNet() PetriNet petriNetV3 = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("3"), superCreator.loggedSuper).getNet() - assert !petriNetService.get(petriNetV1.getObjectId()).isVersionActive() - assert !petriNetService.get(petriNetV2.getObjectId()).isVersionActive() - assert petriNetService.get(petriNetV3.getObjectId()).isVersionActive() + assert !petriNetService.get(petriNetV1.getObjectId()).versionActive() + assert !petriNetService.get(petriNetV2.getObjectId()).versionActive() + assert petriNetService.get(petriNetV3.getObjectId()).versionActive() MakeVersionActiveDTO response = petriNetService.makeVersionActive(petriNetV2.getStringId()) assert response != null assert response.activatedProcessId() == petriNetV2.getStringId() assert response.inactivatedProcessId() == petriNetV3.getStringId() petriNetV2 = petriNetService.get(petriNetV2.getObjectId()) - assert !petriNetService.get(petriNetV1.getObjectId()).isVersionActive() - assert petriNetService.get(petriNetV2.getObjectId()).isVersionActive() - assert !petriNetService.get(petriNetV3.getObjectId()).isVersionActive() + assert !petriNetService.get(petriNetV1.getObjectId()).versionActive() + assert petriNetService.get(petriNetV2.getObjectId()).versionActive() + assert !petriNetService.get(petriNetV3.getObjectId()).versionActive() petriNetV2.makeInactive() petriNetService.save(petriNetV2) @@ -237,9 +237,9 @@ class PetriNetServiceTest { assert response != null assert response.activatedProcessId() == petriNetV3.getStringId() assert response.inactivatedProcessId() == null - assert !petriNetService.get(petriNetV1.getObjectId()).isVersionActive() - assert !petriNetService.get(petriNetV2.getObjectId()).isVersionActive() - assert petriNetService.get(petriNetV3.getObjectId()).isVersionActive() + assert !petriNetService.get(petriNetV1.getObjectId()).versionActive() + assert !petriNetService.get(petriNetV2.getObjectId()).versionActive() + assert petriNetService.get(petriNetV3.getObjectId()).versionActive() } @Test diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java index 04a8177dc9..5f86772a42 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java @@ -24,7 +24,7 @@ public abstract class ElasticPetriNet { private Version version; - private boolean isVersionActive; + private boolean versionActive; private String uriNodeId; @@ -40,7 +40,7 @@ public ElasticPetriNet(PetriNet net) { this.id = net.getStringId(); this.identifier = net.getIdentifier(); this.version = net.getVersion(); - this.isVersionActive = net.isVersionActive(); + this.versionActive = net.isVersionActive(); this.uriNodeId = net.getUriNodeId(); this.title = this.transformToField(net.getTitle()); this.initials = net.getInitials(); @@ -49,7 +49,7 @@ public ElasticPetriNet(PetriNet net) { public void update(ElasticPetriNet net) { this.version = net.getVersion(); - this.isVersionActive = net.isVersionActive(); + this.versionActive = net.isVersionActive(); if (net.getUriNodeId() != null) { this.uriNodeId = net.getUriNodeId(); } diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java index 16e50f1385..a2d5627ac5 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java @@ -76,7 +76,7 @@ public abstract class PetriNet extends PetriNetObject { @Getter @Setter @Indexed - private boolean isVersionActive; + private boolean versionActive; @Getter @Setter @@ -174,7 +174,7 @@ public PetriNet(PetriNet petriNet) { this.title = petriNet.getTitle(); this.importId = petriNet.getImportId(); this.version = petriNet.getVersion(); - this.isVersionActive = petriNet.isVersionActive(); + this.versionActive = petriNet.isVersionActive(); this.defaultCaseName = petriNet.getDefaultCaseName(); this.defaultCaseNameExpression = petriNet.getDefaultCaseNameExpression(); this.initials = petriNet.getInitials(); From 48e1411b3c0baeaa29c68e6af539f41d98a6d750 Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 25 Nov 2025 10:47:33 +0100 Subject: [PATCH 28/40] [NAE-2266] Introduce active version of process - remove makeVersionActive API --- .../petrinet/service/PetriNetService.java | 27 ------------------- .../service/interfaces/IPetriNetService.java | 6 ----- .../petrinet/web/PetriNetController.java | 25 ----------------- .../petrinet/MakeVersionActiveDTO.java | 10 ------- 4 files changed, 68 deletions(-) delete mode 100644 nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/response/petrinet/MakeVersionActiveDTO.java diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index cf4326a483..947181aa9c 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -5,7 +5,6 @@ import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import com.netgrif.application.engine.configuration.properties.CacheConfigurationProperties; import com.netgrif.application.engine.files.minio.StorageConfigurationProperties; -import com.netgrif.application.engine.objects.dto.response.petrinet.MakeVersionActiveDTO; import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; import com.netgrif.application.engine.petrinet.web.responsebodies.ArcImportReference; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; @@ -249,32 +248,6 @@ public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionTyp return outcome; } - @Override - public MakeVersionActiveDTO makeVersionActive(String processId) { - log.info("Activating the process with id [{}]...", processId); - PetriNet processToActivate = self.getPetriNet(processId); - PetriNet processToInactivate = self.getActiveVersionByIdentifier(processToActivate.getIdentifier()); - - if (processToInactivate != null && processToInactivate.getStringId().equals(processToActivate.getStringId())) { - log.debug("The process to activate is already active. Nothing to do"); - return new MakeVersionActiveDTO(); - } - - if (processToInactivate != null) { - log.debug("Inactivating current active version of process with ID [{}] of identifier [{}]", - processToInactivate.getStringId(), processToInactivate.getIdentifier()); - processToInactivate.makeInactive(); - doSaveInternal(processToInactivate); - } - processToActivate.makeActive(); - doSaveInternal(processToActivate); - - log.debug("Successfully activated process with ID [{}] of identifier [{}]", processToActivate.getStringId(), - processToActivate.getIdentifier()); - return new MakeVersionActiveDTO(processToActivate.getStringId(), processToInactivate == null ? null - : processToInactivate.getStringId()); - } - @Override public Optional save(PetriNet petriNet) { return doSaveInternal(petriNet); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java index 245b62bacd..a4494f1d55 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java @@ -1,7 +1,6 @@ package com.netgrif.application.engine.petrinet.service.interfaces; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; -import com.netgrif.application.engine.objects.dto.response.petrinet.MakeVersionActiveDTO; import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; import com.netgrif.application.engine.objects.petrinet.domain.PetriNetSearch; import com.netgrif.application.engine.objects.petrinet.domain.Transition; @@ -111,11 +110,6 @@ static DataFieldReference transformToReference(PetriNet net, Transition transiti */ ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionType releaseType, LoggedUser user, Map params) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException; - /** - * todo javadoc - */ - MakeVersionActiveDTO makeVersionActive(String processId); - /** * Saves a PetriNet object. * diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java index aa001ebed1..eb9cb4f90f 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java @@ -6,7 +6,6 @@ import com.netgrif.application.engine.eventoutcomes.LocalisedEventOutcomeFactory; import com.netgrif.application.engine.importer.service.Importer; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; -import com.netgrif.application.engine.objects.dto.response.petrinet.MakeVersionActiveDTO; import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; import com.netgrif.application.engine.objects.petrinet.domain.PetriNetSearch; import com.netgrif.application.engine.objects.petrinet.domain.VersionType; @@ -40,7 +39,6 @@ import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.PagedModel; import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; @@ -122,29 +120,6 @@ public EntityModel importPetriNet( } } - @PreAuthorize("@authorizationService.hasAuthority('ADMIN')") - @Operation(summary = "Make the process active", - description = "Caller must have the ADMIN role. Makes the provided process active. The previous active version is inactivated.", - security = {@SecurityRequirement(name = "BasicAuth")}) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "OK. The response contains the IDs of process models, that have been activated and inactivated"), - @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), - @ApiResponse(responseCode = "404", description = "Process model is not found") - }) - @PostMapping(value = "/activate/{id}", produces = MediaTypes.HAL_JSON_VALUE) - public ResponseEntity makeVersionActive(@PathVariable("id") String processId) { - try { - MakeVersionActiveDTO response = service.makeVersionActive(processId); - return ResponseEntity.ok(response); - } catch (IllegalArgumentException e) { - log.error("The process with id [{}] was not found.", processId, e); - return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); - } catch (Exception e) { - log.error("Making process [{}] active failed: ", processId, e); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); - } - } - @Operation(summary = "Get all processes", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(produces = MediaTypes.HAL_JSON_VALUE) public ResponseEntity> getAll(@RequestParam(value = "indentifier", required = false) String identifier, @RequestParam(value = "version", required = false) String version, Pageable pageable, Authentication auth, Locale locale) { diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/response/petrinet/MakeVersionActiveDTO.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/response/petrinet/MakeVersionActiveDTO.java deleted file mode 100644 index d4b414ab38..0000000000 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/response/petrinet/MakeVersionActiveDTO.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.netgrif.application.engine.objects.dto.response.petrinet; - -import java.io.Serializable; - -public record MakeVersionActiveDTO(String activatedProcessId, String inactivatedProcessId) implements Serializable { - - public MakeVersionActiveDTO() { - this(null, null); - } -} From b30c88419ff4f4ca7723933998338a897a90a0f0 Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 25 Nov 2025 11:48:14 +0100 Subject: [PATCH 29/40] [NAE-2266] Introduce active version of process - update PetriNetService.deletePetriNet to handle versionActive attribute - implement another test for process deletion --- .../petrinet/service/PetriNetService.java | 9 +++ .../service/PetriNetServiceTest.groovy | 72 +++++++++---------- 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index 947181aa9c..655ab4235c 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -588,6 +588,15 @@ public void deletePetriNet(String processId, LoggedUser loggedUser, boolean forc repository.deleteBy_id(petriNet.getObjectId()); evictCache(petriNet); functionCacheService.reloadCachedFunctions(petriNet); + if (petriNet.isVersionActive()) { + PetriNet toBeActivated = self.getLatestVersionByIdentifier(petriNet.getIdentifier()); + if (toBeActivated != null) { + log.debug("The active version was removed. Activating the latest version of the process [{}] with id [{}]...", + toBeActivated.getIdentifier(), toBeActivated.getStringId()); + toBeActivated.makeActive(); + save(toBeActivated); + } + } publisher.publishEvent(new ProcessDeleteEvent(petriNet, EventPhase.POST)); } diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy index d283ffdc13..6c1e17f130 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy @@ -7,7 +7,6 @@ import com.netgrif.application.engine.elastic.domain.ElasticPetriNetRepository import com.netgrif.application.engine.ipc.TaskApiTest import com.netgrif.application.engine.objects.auth.domain.* import com.netgrif.application.engine.objects.auth.domain.enums.UserState -import com.netgrif.application.engine.objects.dto.response.petrinet.MakeVersionActiveDTO import com.netgrif.application.engine.objects.elastic.domain.ElasticPetriNet import com.netgrif.application.engine.objects.petrinet.domain.PetriNet import com.netgrif.application.engine.objects.petrinet.domain.PetriNetSearch @@ -153,7 +152,7 @@ class PetriNetServiceTest { ImportPetriNetEventOutcome outcome = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("2"), superCreator.loggedSuper) PetriNet petriNetV2 = outcome.getNet() assert petriNetV2 != null - assert petriNetV2.versionActive() + assert petriNetV2.versionActive Version version = new Version() version.setMajor(2) assert petriNetV2.getVersion() == version @@ -188,58 +187,57 @@ class PetriNetServiceTest { petriNetV2.makeActive() petriNetV2 = petriNetService.save(petriNetV2).get() - assert petriNetV2.versionActive() + assert petriNetV2.versionActive petriNetV4.makeInactive() petriNetV4 = petriNetService.save(petriNetV4).get() - assert !petriNetV4.versionActive() + assert !petriNetV4.versionActive outcome = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("5"), superCreator.loggedSuper) PetriNet petriNetV5 = outcome.getNet() assert petriNetV5 != null - assert !petriNetService.get(petriNetV2.getObjectId()).versionActive() - assert !petriNetService.get(petriNetV4.getObjectId()).versionActive() - assert petriNetV5.versionActive() + assert !petriNetService.get(petriNetV2.getObjectId()).versionActive + assert !petriNetService.get(petriNetV4.getObjectId()).versionActive + assert petriNetV5.versionActive version = new Version() version.setMajor(5) assert petriNetV5.getVersion() == version Thread.sleep(5000) elasticPetriNetV2Optional = elasticPetriNetRepository.findById(petriNetV2.stringId) - assert !elasticPetriNetV2Optional.get().versionActive() + assert !elasticPetriNetV2Optional.get().versionActive elasticPetriNetV4Optional = elasticPetriNetRepository.findById(petriNetV4.stringId) - assert !elasticPetriNetV4Optional.get().versionActive() + assert !elasticPetriNetV4Optional.get().versionActive Optional elasticPetriNetV5Optional = elasticPetriNetRepository.findById(petriNetV5.stringId) assert elasticPetriNetV5Optional.isPresent() - assert elasticPetriNetV5Optional.get().versionActive() + assert elasticPetriNetV5Optional.get().versionActive } @Test - void testMakeVersionActive() { - PetriNet petriNetV1 = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("1"), superCreator.loggedSuper).getNet() - PetriNet petriNetV2 = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("2"), superCreator.loggedSuper).getNet() - PetriNet petriNetV3 = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("3"), superCreator.loggedSuper).getNet() - assert !petriNetService.get(petriNetV1.getObjectId()).versionActive() - assert !petriNetService.get(petriNetV2.getObjectId()).versionActive() - assert petriNetService.get(petriNetV3.getObjectId()).versionActive() - - MakeVersionActiveDTO response = petriNetService.makeVersionActive(petriNetV2.getStringId()) - assert response != null - assert response.activatedProcessId() == petriNetV2.getStringId() - assert response.inactivatedProcessId() == petriNetV3.getStringId() - petriNetV2 = petriNetService.get(petriNetV2.getObjectId()) - assert !petriNetService.get(petriNetV1.getObjectId()).versionActive() - assert petriNetService.get(petriNetV2.getObjectId()).versionActive() - assert !petriNetService.get(petriNetV3.getObjectId()).versionActive() - - petriNetV2.makeInactive() - petriNetService.save(petriNetV2) - - response = petriNetService.makeVersionActive(petriNetV3.getStringId()) - assert response != null - assert response.activatedProcessId() == petriNetV3.getStringId() - assert response.inactivatedProcessId() == null - assert !petriNetService.get(petriNetV1.getObjectId()).versionActive() - assert !petriNetService.get(petriNetV2.getObjectId()).versionActive() - assert petriNetService.get(petriNetV3.getObjectId()).versionActive() + void testVersionActiveOnDelete() { + ImportPetriNetEventOutcome outcome = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("1"), superCreator.loggedSuper) + PetriNet processV1 = outcome.getNet() + outcome = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("2"), superCreator.loggedSuper) + PetriNet processV2 = outcome.getNet() + outcome = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("3"), superCreator.loggedSuper) + PetriNet processV3 = outcome.getNet() + + assert !petriNetService.get(processV1.getObjectId()).versionActive + assert !petriNetService.get(processV2.getObjectId()).versionActive + assert petriNetService.get(processV3.getObjectId()).versionActive + + petriNetService.deletePetriNet(processV2.getStringId(), superCreator.loggedSuper) + + assert petriNetRepository.findById(processV2.getStringId()).isEmpty() + assert !petriNetService.get(processV1.getObjectId()).versionActive + assert petriNetService.get(processV3.getObjectId()).versionActive + + petriNetService.deletePetriNet(processV3.getStringId(), superCreator.loggedSuper) + + assert petriNetRepository.findById(processV3.getStringId()).isEmpty() + assert petriNetService.get(processV1.getObjectId()).versionActive + + petriNetService.deletePetriNet(processV1.getStringId(), superCreator.loggedSuper) + + assert petriNetRepository.findById(processV1.getStringId()).isEmpty() } @Test From d7705b2de541bf981a4d593c8b03d77dea2bb74c Mon Sep 17 00:00:00 2001 From: chvostek Date: Thu, 27 Nov 2025 14:20:38 +0100 Subject: [PATCH 30/40] [NAE-2266] Introduce active version of process - mark methods import petriNet method transactional --- .../application/engine/petrinet/service/PetriNetService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index 655ab4235c..c3eabbfcf1 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -178,16 +178,19 @@ public List get(List petriNetIds) { @Override @Deprecated + @Transactional public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, String releaseType, LoggedUser author) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException { return importPetriNet(xmlFile, VersionType.valueOf(releaseType.trim().toUpperCase()), author); } @Override + @Transactional public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionType releaseType, LoggedUser author) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException { return importPetriNet(xmlFile, releaseType, author, new HashMap<>()); } @Override + @Transactional public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionType releaseType, LoggedUser author, Map params) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException { ImportPetriNetEventOutcome outcome = new ImportPetriNetEventOutcome(); ByteArrayOutputStream xmlCopy = new ByteArrayOutputStream(); From 2654b7d631ab9f916b0aa77b9bd192b7fedfadf2 Mon Sep 17 00:00:00 2001 From: chvostek Date: Thu, 27 Nov 2025 14:29:33 +0100 Subject: [PATCH 31/40] [NAE-2266] Introduce active version of process - update javadoc --- .../domain/repositories/PetriNetRepository.java | 6 +++++- .../service/interfaces/IPetriNetService.java | 1 - .../objects/petrinet/domain/version/Version.java | 16 ++++++++++------ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java index bb1d1587ec..41d53b6f0c 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java @@ -34,7 +34,11 @@ public interface PetriNetRepository extends MongoRepository, Q PetriNet findByIdentifierAndVersion(String identifier, Version version); /** - * todo javadoc + * Finds a {@link PetriNet} entity by its identifier and versionActive attribute + * + * @param identifier the unique identifier of the PetriNet. + * @param versionActive if true, the active version will be found, otherwise the inactive version + * @return the {@link PetriNet} entity matching the given identifier and versionActive attribute, or {@code null} if none found. */ PetriNet findByIdentifierAndVersionActive(String identifier, boolean versionActive); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java index a4494f1d55..7ec7bb53bf 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java @@ -154,7 +154,6 @@ static DataFieldReference transformToReference(PetriNet net, Transition transiti /** * Retrieves the active version of a {@link PetriNet} by its identifier. - * todo javadoc isversionactive logic * * @param identifier the unique identifier of the PetriNet * @return the active version of the {@link PetriNet} matching the provided identifier or null if not found diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/version/Version.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/version/Version.java index 0592ab8e21..50dd1f5481 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/version/Version.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/version/Version.java @@ -53,10 +53,10 @@ public void increment(VersionType type) { } /** - * todo javadoc - * 0 equals - * <0 this < other - * >0 this > other + * Compares this version to the other version + * + * @param other other version to be compared with + * @return 0 if the versions equal, <0 if this is lower than other, >0 if this is higher than other */ public int compareTo(Version other) { if (this.major != other.major) { @@ -69,14 +69,18 @@ public int compareTo(Version other) { } /** - * todo javadoc + * Checks if this version is higher than the other + * @param other other version to be compared with + * @return true if this version is higher than the other */ public boolean isHigherThan(Version other) { return compareTo(other) > 0; } /** - * todo javadoc + * Checks if this version is lower than the other + * @param other other version to be compared with + * @return true if this version is lower than the other */ public boolean isLowerThan(Version other) { return compareTo(other) < 0; From 61f9b2ec2d2948fac72797f042673741c643951e Mon Sep 17 00:00:00 2001 From: chvostek Date: Fri, 28 Nov 2025 10:17:24 +0100 Subject: [PATCH 32/40] [NAE-2266] Introduce active version of process - add null checks --- .../domain/dataset/logic/action/ActionDelegate.groovy | 4 ++++ .../logic/action/delegate/RoleActionDelegate.groovy | 6 ++++++ .../engine/menu/services/DashboardItemServiceImpl.java | 7 ++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 9647d2d237..273dce5e92 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -2442,6 +2442,10 @@ class ActionDelegate { temp.put(entry.value, petriNetService.getActiveVersionByIdentifier(entry.value)) } PetriNet net = temp[entry.value] + if (net == null) { + throw new IllegalArgumentException("The process with identifier [%s] could not be found when collecting roles." + .formatted(entry.value)) + } ProcessRole role = net.roles.find { it.value.importId == entry.key }.value return [(role.importId + ":" + net.identifier), ("$role.name ($net.title)" as String)] } diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy index da2f0806ff..a1b8e7b8db 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy @@ -52,6 +52,9 @@ class RoleActionDelegate extends AbstractActionDelegate { AbstractUser assignRole(String roleImportId, String petriNetIdentifier, AbstractUser user = affectedUser) { PetriNet petriNet = petriNetService.getActiveVersionByIdentifier(petriNetIdentifier) + if (petriNet == null) { + throw new IllegalArgumentException("The process with identifier [%s] could not be found".formatted(petriNetIdentifier)) + } assignRole(roleImportId, user, petriNet) } @@ -76,6 +79,9 @@ class RoleActionDelegate extends AbstractActionDelegate { AbstractUser removeRole(String roleImportId, String petriNetIdentifier, AbstractUser user = affectedUser) { PetriNet petriNet = petriNetService.getActiveVersionByIdentifier(petriNetIdentifier) + if (petriNet == null) { + throw new IllegalArgumentException("The process with identifier [%s] could not be found".formatted(petriNetIdentifier)) + } removeRole(roleImportId, user, petriNet) } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java b/application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java index 8d40f6cb91..aeba0a4e6c 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java @@ -8,6 +8,7 @@ import com.netgrif.application.engine.objects.auth.domain.AbstractUser; import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; +import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; import com.netgrif.application.engine.objects.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.objects.utils.MenuItemUtils; import com.netgrif.application.engine.objects.workflow.domain.Case; @@ -62,7 +63,11 @@ public Case getOrCreate(DashboardItemBody body) throws TransitionNotExecutableEx } LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedOrSystem()); - itemCase = workflowService.createCase(petriNetService.getActiveVersionByIdentifier(DashboardItemConstants.PROCESS_IDENTIFIER).getStringId(), body.getName().getDefaultValue(), "", loggedUser).getCase(); + PetriNet petriNet = petriNetService.getActiveVersionByIdentifier(DashboardItemConstants.PROCESS_IDENTIFIER); + if (petriNet == null) { + throw new IllegalStateException("Dashboard item process not found or not active"); + } + itemCase = workflowService.createCase(petriNet.getStringId(), body.getName().getDefaultValue(), "", loggedUser).getCase(); ToDataSetOutcome outcome = body.toDataSet(); itemCase = setDataWithExecute(itemCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); return itemCase; From 289de0fa6efa8785a456bac245de4298f7950215 Mon Sep 17 00:00:00 2001 From: chvostek Date: Fri, 28 Nov 2025 10:19:16 +0100 Subject: [PATCH 33/40] [NAE-2266] Introduce active version of process - add null check --- .../menu/services/DashboardManagementServiceImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java b/application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java index a449783116..4326840671 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java @@ -9,6 +9,7 @@ import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import com.netgrif.application.engine.objects.petrinet.domain.I18nString; +import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; import com.netgrif.application.engine.objects.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.objects.utils.MenuItemUtils; import com.netgrif.application.engine.objects.workflow.domain.Case; @@ -64,7 +65,11 @@ public Case createDashboardManagement(DashboardManagementBody body) throws Trans } addReferencedMenuItems(body); LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedOrSystem()); - managementCase = workflowService.createCase(petriNetService.getActiveVersionByIdentifier(DashboardManagementConstants.PROCESS_IDENTIFIER).getStringId(), body.getName().getDefaultValue(), "", loggedUser).getCase(); + PetriNet petriNet = petriNetService.getActiveVersionByIdentifier(DashboardManagementConstants.PROCESS_IDENTIFIER); + if (petriNet == null) { + throw new IllegalStateException("Dashboard management process not found or not active"); + } + managementCase = workflowService.createCase(petriNet.getStringId(), body.getName().getDefaultValue(), "", loggedUser).getCase(); ToDataSetOutcome outcome = body.toDataSet(); managementCase = setDataWithExecute(managementCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); return managementCase; From e9ef49401b463c62953596f633c317e0c49cdbd9 Mon Sep 17 00:00:00 2001 From: chvostek Date: Fri, 28 Nov 2025 10:21:04 +0100 Subject: [PATCH 34/40] [NAE-2266] Introduce active version of process - update javadoc --- .../engine/petrinet/service/interfaces/IPetriNetService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java index 7ec7bb53bf..a1c066b143 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java @@ -164,7 +164,7 @@ static DataFieldReference transformToReference(PetriNet net, Transition transiti * Retrieves the latest version of a {@link PetriNet} by its identifier. * * @param identifier the unique identifier of the PetriNet - * @return the latest version of the {@link PetriNet} matching the provided identifier + * @return the latest version of the {@link PetriNet} matching the provided identifier or null if not found */ PetriNet getLatestVersionByIdentifier(String identifier); From 2274d62d3a216fcc21a56b789bcdd71c263e3908 Mon Sep 17 00:00:00 2001 From: chvostek Date: Fri, 28 Nov 2025 10:25:44 +0100 Subject: [PATCH 35/40] [NAE-2266] Introduce active version of process - add null check --- .../engine/workflow/service/FilterImportExportService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java index 54dfed8b6c..cfc56b6468 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java @@ -267,6 +267,9 @@ public void changeFilterField(Collection filterFields) { Task importedFilterTask = taskService.findOne(f); Case filterCase = workflowService.findOne(importedFilterTask.getCaseId()); PetriNet filterNet = petriNetService.getActiveVersionByIdentifier(FILTER_NET_IDENTIFIER); + if (filterNet == null) { + throw new IllegalStateException("No filter process found or active"); + } List requiredNets = filterCase.getDataSet().get(FIELD_FILTER).getAllowedNets(); List currentNets = petriNetService.getExistingPetriNetIdentifiersFromIdentifiersList(requiredNets); if (currentNets.size() < requiredNets.size()) { From 174c7ee6f4fa6bafc2b77714159c754994fd8fbc Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 16 Dec 2025 14:48:53 +0100 Subject: [PATCH 36/40] [NAE-2212] Global scope process function caches - removed ReadOnlyCache.java, the use of this is not needed, because global scoped functions will be read from admin - removed WorkerConcurrentCacheManager because the ReadOnlyCache is not used - refactored name of namespace scoped functions to "global" - deprecated "namespace" function scope --- .../configuration/CacheConfiguration.java | 6 +-- .../configuration/cache/ReadOnlyCache.java | 42 ------------------- .../cache/WorkerConcurrentCacheManager.java | 39 ----------------- .../RunnerConfigurationProperties.java | 2 +- .../importer/service/FunctionFactory.java | 11 +++++ .../service/FieldActionsCacheService.java | 18 ++++---- .../interfaces/IFieldActionsCacheService.java | 2 +- .../application/engine/TestHelper.groovy | 2 +- .../petrinet/domain/FunctionScope.java | 16 ++++++- 9 files changed, 39 insertions(+), 99 deletions(-) delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/ReadOnlyCache.java delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java index 7fed1391bb..55909a8a98 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java @@ -1,17 +1,15 @@ package com.netgrif.application.engine.configuration; -import com.netgrif.application.engine.configuration.cache.WorkerConcurrentCacheManager; import com.netgrif.application.engine.configuration.properties.CacheConfigurationProperties; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.concurrent.ConcurrentMapCacheManager; import org.springframework.cache.interceptor.CacheResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; -import java.util.Set; - @Configuration @EnableCaching @@ -27,7 +25,7 @@ public CacheConfiguration(CacheConfigurationProperties properties) { @Primary @Override public CacheManager cacheManager() { - return new WorkerConcurrentCacheManager(Set.of(properties.getNamespaceFunctions()), properties.getAllCaches().toArray(String[]::new)); + return new ConcurrentMapCacheManager(properties.getAllCaches().toArray(String[]::new)); } @Bean diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/ReadOnlyCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/ReadOnlyCache.java deleted file mode 100644 index 10fae378d6..0000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/ReadOnlyCache.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.netgrif.application.engine.configuration.cache; - -import org.springframework.cache.Cache; - -import java.util.concurrent.Callable; - -public class ReadOnlyCache implements Cache { - - private final Cache cacheDelegate; - - public ReadOnlyCache(Cache cacheDelegate) { - this.cacheDelegate = cacheDelegate; - } - - @Override - public String getName() { return cacheDelegate.getName(); } - - @Override - public Object getNativeCache() { return cacheDelegate.getNativeCache(); } - - @Override - public ValueWrapper get(Object key) { return cacheDelegate.get(key); } - - @Override - public T get(Object key, Class type) { return cacheDelegate.get(key, type); } - - @Override - public T get(Object key, Callable loader) { return cacheDelegate.get(key, loader); } - - @Override - public void put(Object key, Object value) { cacheDelegate.put(key, value); } - - @Override - public void evict(Object key) { - throw new UnsupportedOperationException("Evict not allowed on worker for " + getName()); - } - - @Override - public void clear() { - throw new UnsupportedOperationException("Clear not allowed on worker for " + getName()); - } -} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java deleted file mode 100644 index ffa8cd3c30..0000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/cache/WorkerConcurrentCacheManager.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.netgrif.application.engine.configuration.cache; - -import org.springframework.cache.Cache; -import org.springframework.cache.concurrent.ConcurrentMapCacheManager; - -import java.util.Objects; -import java.util.Set; - -public class WorkerConcurrentCacheManager extends ConcurrentMapCacheManager { - private final Set readOnlyCacheNames; - - public WorkerConcurrentCacheManager() { - super(); - this.readOnlyCacheNames = Set.of(); - } - - public WorkerConcurrentCacheManager(Set readOnlyCacheNames) { - super(); - this.readOnlyCacheNames = Set.copyOf( - Objects.requireNonNull(readOnlyCacheNames, "readOnlyCacheNames must not be null.") - ); - } - - public WorkerConcurrentCacheManager(Set readOnlyCacheNames, String... cacheNames) { - super(cacheNames); - this.readOnlyCacheNames = Set.copyOf( - Objects.requireNonNull(readOnlyCacheNames, "readOnlyCacheNames must not be null.") - ); - } - - @Override - protected Cache createConcurrentMapCache(String name) { - Cache base = super.createConcurrentMapCache(name); - if (readOnlyCacheNames != null && readOnlyCacheNames.contains(name)) { - return new ReadOnlyCache(base); - } - return base; - } -} \ No newline at end of file diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/RunnerConfigurationProperties.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/RunnerConfigurationProperties.java index ffe6cdd3a3..a2767a77c8 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/RunnerConfigurationProperties.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/RunnerConfigurationProperties.java @@ -56,7 +56,7 @@ public static class FieldRunnerProperties { /** * The size of the cache used for maintaining field runner namespaces. */ - private int namespaceCacheSize = 500; + private int globalFunctionsCacheSize = 500; } /** diff --git a/application-engine/src/main/java/com/netgrif/application/engine/importer/service/FunctionFactory.java b/application-engine/src/main/java/com/netgrif/application/engine/importer/service/FunctionFactory.java index 6847114675..a24ca41f49 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/importer/service/FunctionFactory.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/importer/service/FunctionFactory.java @@ -1,5 +1,6 @@ package com.netgrif.application.engine.importer.service; +import com.netgrif.application.engine.objects.importer.model.Scope; import com.netgrif.application.engine.objects.petrinet.domain.Function; import com.netgrif.application.engine.objects.petrinet.domain.FunctionScope; import lombok.extern.slf4j.Slf4j; @@ -9,7 +10,10 @@ @Slf4j public final class FunctionFactory { + private static final String NAMESPACE = "namespace"; + public Function getFunction(com.netgrif.application.engine.objects.importer.model.Function function) { + checkDeprecatedAttributes(function); Function function1 = new Function(); function1.setDefinition(function.getValue()); @@ -18,4 +22,11 @@ public Function getFunction(com.netgrif.application.engine.objects.importer.mode return function1; } + + private void checkDeprecatedAttributes(com.netgrif.application.engine.objects.importer.model.Function function) { + if (function.getScope() != null && function.getScope().name().equals(NAMESPACE)) { + log.warn("Function scope [NAMESPACE] is deprecated. Replacing with [GLOBAL].]"); + function.setScope(Scope.GLOBAL); + } + } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java index 51432b96c8..6ac9fd3ffa 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java @@ -34,7 +34,7 @@ public class FieldActionsCacheService implements IFieldActionsCacheService { private IPetriNetService petriNetService; private Map actionsCache; - private Map> namespaceFunctionsCache; + private Map> globalFunctionsCache; private Map functionsCache; private final GroovyShell shell; @@ -42,7 +42,7 @@ public FieldActionsCacheService(RunnerConfigurationProperties.FieldRunnerPropert this.properties = properties; this.actionsCache = new MaxSizeHashMap<>(properties.getActionCacheSize()); this.functionsCache = new MaxSizeHashMap<>(properties.getFunctionsCacheSize()); - this.namespaceFunctionsCache = new MaxSizeHashMap<>(properties.getNamespaceCacheSize()); + this.globalFunctionsCache = new MaxSizeHashMap<>(properties.getGlobalFunctionsCacheSize()); this.shell = shellFactory.getGroovyShell(); } @@ -58,21 +58,21 @@ public void cachePetriNetFunctions(PetriNet petriNet) { return; } - List functions = petriNet.getFunctions(FunctionScope.NAMESPACE).stream() + List functions = petriNet.getFunctions(FunctionScope.GLOBAL).stream() .map(function -> CachedFunction.build(shell, function)) .collect(Collectors.toList()); if (!functions.isEmpty()) { evaluateCachedFunctions(functions); - namespaceFunctionsCache.put(petriNet.getIdentifier(), functions); + globalFunctionsCache.put(petriNet.getIdentifier(), functions); } else { - namespaceFunctionsCache.remove(petriNet.getIdentifier()); + globalFunctionsCache.remove(petriNet.getIdentifier()); } } @Override public void reloadCachedFunctions(PetriNet petriNet) { - namespaceFunctionsCache.remove(petriNet.getIdentifier()); + globalFunctionsCache.remove(petriNet.getIdentifier()); cachePetriNetFunctions(petriNetService.getNewestVersionByIdentifier(petriNet.getIdentifier())); } @@ -159,7 +159,7 @@ private String stringifyParameterTypes(Class[] a) { @Override public Map> getNamespaceFunctionCache() { - return new HashMap<>(namespaceFunctionsCache); + return new HashMap<>(globalFunctionsCache); } @Override @@ -168,8 +168,8 @@ public void clearActionCache() { } @Override - public void clearNamespaceFunctionCache() { - this.namespaceFunctionsCache = new MaxSizeHashMap<>(properties.getNamespaceCacheSize()); + public void clearGlobalFunctionCache() { + this.globalFunctionsCache = new MaxSizeHashMap<>(properties.getGlobalFunctionsCacheSize()); } @Override diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java index 10eb67bc21..73fb2401d7 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java @@ -25,7 +25,7 @@ public interface IFieldActionsCacheService { void clearActionCache(); - void clearNamespaceFunctionCache(); + void clearGlobalFunctionCache(); void cacheAllPetriNetsFunctions(); diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/TestHelper.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/TestHelper.groovy index 7c9c495ab7..f6921558ab 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/TestHelper.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/TestHelper.groovy @@ -93,7 +93,7 @@ class TestHelper { roleService.clearCache() actionsCacheService.clearActionCache() actionsCacheService.clearFunctionCache() - actionsCacheService.clearNamespaceFunctionCache() + actionsCacheService.clearGlobalFunctionCache() petriNetService.evictAllCaches() defaultRoleRunner.run() diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/FunctionScope.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/FunctionScope.java index 3d22260234..55f4ee7d02 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/FunctionScope.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/FunctionScope.java @@ -1,9 +1,21 @@ package com.netgrif.application.engine.objects.petrinet.domain; +/** + * Enum representing the scope of a function. + *

+ * This enumeration defines the possible scopes in which a function can operate. + */ public enum FunctionScope { - NAMESPACE("namespace"), - PROCESS("process"); + /** + * Represents the process-specific scope of a function. + */ + PROCESS("process"), + + /** + * Represents the global scope of a function. + */ + GLOBAL("global"); private final String value; From 44c6ed72a1bf5a0654605e530f11b418e5ae27eb Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Thu, 18 Dec 2025 09:19:21 +0100 Subject: [PATCH 37/40] [NAE-2212] Global scope process function caches - renamed function in IFieldActionsCacheService according to PR --- .../engine/workflow/service/FieldActionsCacheService.java | 2 +- .../workflow/service/interfaces/IFieldActionsCacheService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java index 2852edd0fb..608bfed724 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java @@ -99,7 +99,7 @@ public List getCachedFunctions(List page = petriNetService.getAll(pageable); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java index 73fb2401d7..530845928a 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java @@ -27,7 +27,7 @@ public interface IFieldActionsCacheService { void clearGlobalFunctionCache(); - void cacheAllPetriNetsFunctions(); + void cacheAllPetriNetFunctions(); void clearFunctionCache(); } From fc637ee7ef05a8a50cfbbb8fb767b4ef867f52cd Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Thu, 18 Dec 2025 09:49:50 +0100 Subject: [PATCH 38/40] [NAE-2212] Global scope process function caches Updated method and variable names from "namespace functions" to "global functions" across relevant classes for better clarity and consistency. This includes changes in `FieldActionsCacheService`, `IFieldActionsCacheService`, and cache configuration properties. --- .../domain/dataset/logic/action/FieldActionsRunner.groovy | 2 +- .../properties/CacheConfigurationProperties.java | 4 ++-- .../engine/workflow/service/FieldActionsCacheService.java | 2 +- .../service/interfaces/IFieldActionsCacheService.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/FieldActionsRunner.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/FieldActionsRunner.groovy index b4b1c6ba7a..64b567ab4d 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/FieldActionsRunner.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/FieldActionsRunner.groovy @@ -77,7 +77,7 @@ abstract class FieldActionsRunner { actionsCacheService.getCachedFunctions(functions).each { actionDelegate.metaClass."${it.function.name}" << it.code } - actionsCacheService.getNamespaceFunctionCache().each { entry -> + actionsCacheService.getGlobalFunctionsCache().each { entry -> def namespace = [:] entry.getValue().each { namespace["${it.function.name}"] = it.code.rehydrate(actionDelegate, it.code.owner, it.code.thisObject) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java index b3346e8e32..577b6992d2 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java @@ -49,7 +49,7 @@ public class CacheConfigurationProperties { /** * Default cache name for caching namespace functions. */ - private String namespaceFunctions = "namespaceFunctions"; + private String globalFunctions = "globalFunctions"; /** * A list of additional custom cache names. @@ -65,7 +65,7 @@ public class CacheConfigurationProperties { */ public Set getAllCaches() { Set caches = new LinkedHashSet<>(Arrays.asList(petriNetById, petriNetByIdentifier, petriNetActive, - petriNetLatest, petriNetCache, loadedModules, namespaceFunctions)); + petriNetLatest, petriNetCache, loadedModules, globalFunctions)); caches.addAll(additional); return caches; } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java index 608bfed724..c35c6dd38f 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java @@ -158,7 +158,7 @@ private String stringifyParameterTypes(Class[] a) { } @Override - public Map> getNamespaceFunctionCache() { + public Map> getGlobalFunctionsCache() { return new HashMap<>(globalFunctionsCache); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java index 530845928a..9e868e3aa6 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java @@ -19,7 +19,7 @@ public interface IFieldActionsCacheService { List getCachedFunctions(List functions); - Map> getNamespaceFunctionCache(); + Map> getGlobalFunctionsCache(); void evaluateFunctions(List functions); From 6d84f5b89d9e4b6216cc3f64fed6e8836088592d Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Thu, 18 Dec 2025 11:08:22 +0100 Subject: [PATCH 39/40] Remove redundant dependencies and fix scope comparison logic. Removed duplicate JUnit dependencies from the pom.xml to clean up the build configuration. Corrected the scope comparison logic in FunctionFactory to use the proper `value()` method instead of `name()`. --- application-engine/pom.xml | 12 ------------ .../engine/importer/service/FunctionFactory.java | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/application-engine/pom.xml b/application-engine/pom.xml index a9669b0e11..d63f7f0177 100644 --- a/application-engine/pom.xml +++ b/application-engine/pom.xml @@ -270,18 +270,6 @@ ${junit-jupiter.version} test - - org.junit.jupiter - junit-jupiter-api - ${junit-jupiter.version} - test - - - org.junit.jupiter - junit-jupiter-engine - ${junit-jupiter.version} - test - diff --git a/application-engine/src/main/java/com/netgrif/application/engine/importer/service/FunctionFactory.java b/application-engine/src/main/java/com/netgrif/application/engine/importer/service/FunctionFactory.java index a24ca41f49..a12de3db55 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/importer/service/FunctionFactory.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/importer/service/FunctionFactory.java @@ -24,7 +24,7 @@ public Function getFunction(com.netgrif.application.engine.objects.importer.mode } private void checkDeprecatedAttributes(com.netgrif.application.engine.objects.importer.model.Function function) { - if (function.getScope() != null && function.getScope().name().equals(NAMESPACE)) { + if (function.getScope() != null && function.getScope().value().equals(NAMESPACE)) { log.warn("Function scope [NAMESPACE] is deprecated. Replacing with [GLOBAL].]"); function.setScope(Scope.GLOBAL); } From f7b61be7b113679023abe726a3c81e9b1c4380ae Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Thu, 18 Dec 2025 13:53:29 +0100 Subject: [PATCH 40/40] Refactor caching logic in FieldActionsCacheService and improve annotations. Updated method names and cache references to align with global function terminology, ensuring consistency across the service. Added proper type annotations in GenericMapCache and replaced `CachingConfigurerSupport` with `CachingConfigurer` for better configuration clarity and flexibility. --- .../engine/configuration/CacheConfiguration.java | 4 ++-- .../engine/configuration/GenericMapCache.java | 5 +++-- .../workflow/service/FieldActionsCacheService.java | 10 +++++----- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java index 41fc847914..bce3b543d9 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java @@ -8,7 +8,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; -import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.CachingConfigurer; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.concurrent.ConcurrentMapCache; import org.springframework.cache.interceptor.CacheResolver; @@ -25,7 +25,7 @@ @Configuration @EnableCaching @RequiredArgsConstructor -public class CacheConfiguration extends CachingConfigurerSupport { +public class CacheConfiguration implements CachingConfigurer { private final RunnerConfigurationProperties.FieldRunnerProperties fieldRunnerProperties; private final CacheConfigurationProperties properties; diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java index d2a12aecc1..199700182e 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java @@ -1,6 +1,7 @@ package com.netgrif.application.engine.configuration; import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; import org.springframework.cache.Cache; import org.springframework.cache.support.SimpleValueWrapper; @@ -25,9 +26,9 @@ public GenericMapCache(String name, Class valueType, java.util.function.Suppl this.atomicMapRef = new AtomicReference<>(mapFactory.get()); } - @Override public String getName() { return name; } + @Override public @NotNull String getName() { return name; } - @Override public Object getNativeCache() { return Map.copyOf(map()); } + @Override public @NotNull Object getNativeCache() { return Map.copyOf(map()); } @Override public T get(Object key, Callable loader) { diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java index 9c1450fd8d..7b8de9db8e 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java @@ -58,20 +58,20 @@ public void cachePetriNetFunctions(PetriNet petriNet) { .map(function -> CachedFunction.build(shell, function)) .collect(Collectors.toList()); - Cache namespaceFunctionsCache = getRequiredCache(properties.getNamespaceFunctions()); + Cache globalFunctionsCache = getRequiredCache(properties.getGlobalFunctions()); if (!functions.isEmpty()) { evaluateCachedFunctions(functions); globalFunctionsCache.put(petriNet.getIdentifier(), functions); } else { - globalFunctionsCache.remove(petriNet.getIdentifier()); + globalFunctionsCache.evictIfPresent(petriNet.getIdentifier()); } } @Override public void reloadCachedFunctions(String petriNetId) { - getRequiredCache(properties.getNamespaceFunctions()).evictIfPresent(petriNetId); - cachePetriNetFunctions(petriNetService.getNewestVersionByIdentifier(petriNetId)); + getRequiredCache(properties.getGlobalFunctions()).evictIfPresent(petriNetId); + cachePetriNetFunctions(petriNetService.getActiveVersionByIdentifier(petriNetId)); } @Override @@ -172,7 +172,7 @@ private String stringifyParameterTypes(Class[] a) { @Override public Map> getGlobalFunctionsCache() { - return new HashMap<>((Map) getRequiredCache(properties.getGlobalFunctions()).getNativeCache()); + return new HashMap<>((Map>) getRequiredCache(properties.getGlobalFunctions()).getNativeCache()); } @Override