-
Notifications
You must be signed in to change notification settings - Fork 6
[NAE-2182] Clearing actions cache #348
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release/7.0.0-rev9
Are you sure you want to change the base?
Conversation
…ache endpoints for actionsCache, functionsCache and namespaceFunctionsCache
WalkthroughReplaces simple cache manager with a SimpleCacheManager that includes per-name ConcurrentMapCache plus bounded GenericMapCache entries for actions/functions; adds CacheMapKeys and cache properties; refactors FieldActionsCacheService to use CacheManager and reload by PetriNet id; renames/introduces PetriNet active/latest APIs and related callers; exposes all actuator endpoints. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Caller
participant FieldActionsCacheService as FACS
participant CacheManager
participant ActionsCache as "Cache(actions)"
participant PetriNetService as PNS
Caller->>FACS: getCompiledAction(fieldId, actionRef)
FACS->>CacheManager: getCache("actions")
CacheManager-->>FACS: ActionsCache
FACS->>ActionsCache: get(key, loader)
alt cache hit
ActionsCache-->>FACS: Closure
else cache miss
ActionsCache-->>FACS: miss
FACS->>PNS: (may call) getActiveVersionByIdentifier(petriNetId)
PNS-->>FACS: PetriNet
FACS->>FACS: compile Closure via Groovy shell
FACS->>ActionsCache: put(key, compiledClosure)
ActionsCache-->>FACS: stored Closure
end
FACS-->>Caller: Closure
sequenceDiagram
autonumber
participant Caller
participant FACS
participant CacheManager
participant GlobalFuncs as "Cache(globalFunctions)"
participant PNS
Caller->>FACS: reloadCachedFunctions(petriNetId)
FACS->>CacheManager: getCache("globalFunctions")
CacheManager-->>FACS: GlobalFuncs
FACS->>GlobalFuncs: evict(petriNetId)
FACS->>PNS: getActiveVersionByIdentifier(petriNetId)
PNS-->>FACS: PetriNet (or null)
alt PetriNet found
FACS->>FACS: evaluate functions
FACS->>GlobalFuncs: put(petriNetId, evaluatedFunctions)
else not found
FACS-->Caller: no-op / throw (depending on flow)
end
FACS-->>Caller: void
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Potential focus areas:
Possibly related PRs
Pre-merge checks❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
🧹 Nitpick comments (6)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
3-7: Make utility holder non-instantiable and final.Prevents accidental instantiation/extension and clarifies intent.
-public class CacheMapKeys { +public final class CacheMapKeys { public static final String ACTIONS = "actionsCache"; public static final String FUNCTIONS = "functionsCache"; public static final String NAMESPACE_FUNCTIONS = "namespaceFunctionsCache"; + + private CacheMapKeys() { + throw new AssertionError("No instances"); + } }application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
22-24: Leaking the mutable Map via getNativeCache couples callers to internals.FieldActionsCacheService manipulating the map directly bypasses cache invariants/locking.
Prefer cache APIs (
get/put/evict/clear). If direct access is required, document it and ensure all such usage is confined and synchronized.application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)
43-65: Confirm thread-safety of MaxSizeHashMap or wrap it.If
MaxSizeHashMapis not concurrent, concurrent access through the cache will race.Options:
- Wrap:
() -> java.util.Collections.synchronizedMap(new MaxSizeHashMap<>(...))- Or rely on the synchronized GenericMapCache changes above.
- Add a brief Javadoc on
MaxSizeHashMapusage here clarifying concurrency expectations.application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (3)
142-143: Avoid raw cast in getNamespaceFunctionCache; ensure type-safety and null-safety.Keeps method contract explicit and prevents CCE if the impl changes.
- return new HashMap<>((Map) cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS).getNativeCache()); + Map<String, List<CachedFunction>> nativeMap = + (Map<String, List<CachedFunction>>) getRequiredCache(CacheMapKeys.NAMESPACE_FUNCTIONS).getNativeCache(); + return new HashMap<>(nativeMap);
147-148: Clear methods: use guarded cache retrieval.- cacheManager.getCache(CacheMapKeys.ACTIONS).clear(); + getRequiredCache(CacheMapKeys.ACTIONS).clear(); @@ - cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS).clear(); + getRequiredCache(CacheMapKeys.NAMESPACE_FUNCTIONS).clear(); @@ - cacheManager.getCache(CacheMapKeys.FUNCTIONS).clear(); + getRequiredCache(CacheMapKeys.FUNCTIONS).clear();Also applies to: 151-153, 156-158
74-84: Keep this service cache-implementation-agnostic; avoidgetNativeCache()andinstanceof Map.Relying on the native Map couples you to
GenericMapCacheand its thread-safety characteristics. UsingCacheAPIs (get(key, Callable),evict,clear) preserves portability and simplifies reasoning.Also applies to: 89-99, 142-143
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java(4 hunks)application-engine/src/main/resources/application.yaml(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
CacheMapKeys(3-7)
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (2)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
CacheMapKeys(3-7)application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
Slf4j(8-78)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: Build
- GitHub Check: task-list-completed
🔇 Additional comments (3)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (2)
67-70: CacheManager wiring LGTM.Unified
SimpleCacheManagerwith default and specialized caches is clean and extensible.
37-41: Exclude specialized cache names from default caches. Filter outCacheMapKeys.ACTIONS,CacheMapKeys.FUNCTIONSandCacheMapKeys.NAMESPACE_FUNCTIONSfrom theproperties.getAllCaches()stream before mapping toConcurrentMapCacheto avoid registering those caches twice.application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (1)
28-37: Verify FieldActionsCacheService bean registration
Confirm that FieldActionsCacheService is annotated (e.g. @service or @component) or declared via a @bean method so Spring will pick up the new constructor signature, and search your configuration for any @bean methods or manual instantiations of FieldActionsCacheService using the old two-arg constructor.
...ation-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java
Show resolved
Hide resolved
...ation-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java
Outdated
Show resolved
Hide resolved
...ation-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java
Outdated
Show resolved
Hide resolved
.../src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
Outdated
Show resolved
Hide resolved
.../src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
Outdated
Show resolved
Hide resolved
.../src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
Show resolved
Hide resolved
| 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()); | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid native Map access in getCachedFunctions; compute-if-absent via Cache API.
Works with any Cache impl and removes silent empty-return if native cache isn’t a Map.
- 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());
- });
- }
+ Cache functionsCache = getRequiredCache(CacheMapKeys.FUNCTIONS);
+ for (Function function : functions) {
+ CachedFunction cf = functionsCache.get(function.getStringId(),
+ () -> CachedFunction.build(shell, function));
+ cachedFunctions.add(cf);
+ }Committable suggestion skipped: line range outside the PR's diff.
| exclude: shutdown | ||
| include: "*" | ||
| endpoint: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Actuator exposure is fully open; combined with anonymous '/manage/' this is a security risk.**
include: "*" exposes sensitive endpoints (env, beans, heapdump, caches). Your config also lists /manage/** under anonymous exceptions (Lines 87–88), making these endpoints publicly accessible.
Apply both changes:
- anonymous-exceptions: /api/auth/signup,/api/auth/token/verify,/api/auth/reset,/api/auth/recover,/manage/**
+ anonymous-exceptions: /api/auth/signup,/api/auth/token/verify,/api/auth/reset,/api/auth/recover exposure:
exclude: shutdown
- include: "*"
+ include: health,info,metrics,prometheus,cachesOptional hardening:
- Require auth/role for /manage/** or move management to a separate port.
- Only include the endpoints you actually need in non-dev profiles.
🤖 Prompt for AI Agents
In application-engine/src/main/resources/application.yaml around lines 119–121
(and note anonymous /manage/** at lines 87–88), the actuator exposure is set to
include: "*" which together with anonymous access to /manage/** exposes
sensitive endpoints; change the actuator exposure to list only the required
endpoints (e.g., health, info, metrics) instead of "*" and remove or restrict
the anonymous exception for /manage/**—either require authentication/appropriate
role for /manage/** or move management endpoints to a separate management port;
make these config edits in the relevant profile(s) and/or add security
constraints so management endpoints are not publicly accessible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 19
♻️ Duplicate comments (5)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
15-15: Make map-backed cache thread-safe; compute-and-store must be atomic.Backed by a plain Map with unsynchronized mutations; races with put/evict/clear and get(loader). Clear also reassigns the map mid-flight. Synchronize mutations and use double-checked locking in get(loader).
public abstract class GenericMapCache<V> implements Cache { @@ - protected volatile Map<String, V> map; + protected volatile Map<String, V> map; + private final Object monitor = new Object(); @@ - @Override - public <T> T get(Object key, java.util.concurrent.Callable<T> loader) { + @Override + public <T> T get(Object key, java.util.concurrent.Callable<T> loader) { String stringKey = String.valueOf(key); - Object present = map.get(stringKey); + 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); - } + synchronized (monitor) { + 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); + } + } } @@ - public void put(Object key, Object value) { - map.put(String.valueOf(key), safeCast(value)); + public void put(Object key, Object value) { + synchronized (monitor) { + map.put(String.valueOf(key), safeCast(value)); + } } @@ - public void evict(Object key) { - map.remove(String.valueOf(key)); + public void evict(Object key) { + synchronized (monitor) { + map.remove(String.valueOf(key)); + } } @@ - public void clear() { - this.map = mapFactory.get(); + public void clear() { + synchronized (monitor) { + this.map = mapFactory.get(); + } log.info("{} cache cleared", this.getName()); }Also applies to: 33-51, 53-67
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (4)
55-62: Fail fast on missing cache; avoid NPE.- Cache namespaceFunctionsCache = cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS); + Cache namespaceFunctionsCache = getRequiredCache(CacheMapKeys.NAMESPACE_FUNCTIONS); @@ - namespaceFunctionsCache.evictIfPresent(petriNet.getIdentifier()); + namespaceFunctionsCache.evictIfPresent(petriNet.getIdentifier());Support code (add once in this class):
private Cache getRequiredCache(String name) { Cache cache = cacheManager.getCache(name); if (cache == null) throw new IllegalStateException("Cache '" + name + "' not configured"); return cache; }
65-69: Guard cache retrieval and reload atomically.- public void reloadCachedFunctions(String petriNetId) { - cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS).evictIfPresent(petriNetId); - cachePetriNetFunctions(petriNetService.getNewestVersionByIdentifier(petriNetId)); - } + public void reloadCachedFunctions(String petriNetId) { + getRequiredCache(CacheMapKeys.NAMESPACE_FUNCTIONS).evictIfPresent(petriNetId); + cachePetriNetFunctions(petriNetService.getNewestVersionByIdentifier(petriNetId)); + }
79-89: Use Cache APIs; avoid peeking into native Map and eliminate TOCTOU.- 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 (Closure) actionsCache.get(stringId).get(); + Cache actionsCache = getRequiredCache(CacheMapKeys.ACTIONS); + if (shouldRewriteCachedActions) { + actionsCache.evict(stringId); + } + return actionsCache.get(stringId, + () -> (Closure) shell.evaluate("{-> " + action.getDefinition() + "}"));
93-105: Compute functions atomically and avoid native cache access.- 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()); - }); - } + Cache functionsCache = getRequiredCache(CacheMapKeys.FUNCTIONS); + for (Function function : functions) { + CachedFunction cf = functionsCache.get(function.getStringId(), + () -> CachedFunction.build(shell, function)); + cachedFunctions.add(cf); + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (9)
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java(3 hunks)application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java(2 hunks)application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java(4 hunks)application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-31T23:40:46.499Z
Learnt from: tuplle
PR: netgrif/application-engine#334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.
Applied to files:
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.javaapplication-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
🧬 Code graph analysis (3)
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (2)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
Slf4j(10-80)application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
Slf4j(66-581)
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (4)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
CacheMapKeys(3-7)application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (1)
Slf4j(10-41)application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
Slf4j(10-80)application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
Slf4j(66-581)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (1)
Slf4j(10-41)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Test
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
🔇 Additional comments (6)
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java (1)
4-4: No action on wildcard import.Acceptable here unless the project enforces “no wildcard imports”.
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (2)
8-8: No action on wildcard import.Safe refactor.
558-569: Add index on functions._id in petriNet collection
The query in PetriNetService.findByFunctionId filters on functions._id; without an index this will trigger a full collection scan. Verify (via @indexed on Function.objectId, a database migration, or manual index setup) that an index exists for functions._id.application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java (1)
15-17: Ignore namespace vs PetriNet ID key concernAll cache operations on
NAMESPACE_FUNCTIONSusepetriNet.getIdentifier()as the key (see FieldActionsCacheService.java:55 and 67), so the cache is intentionally keyed by PetriNet ID. No changes required.Likely an incorrect or invalid review comment.
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (1)
13-13: Confirm reload key semantics
reloadCachedFunctions(stringKey)presumesstringKeyis the Petri net identifier the service expects. Please confirm the key format aligns withIFieldActionsCacheService’s contract; otherwise, misses will never populate.I can generate a short repo scan to locate all
reloadCachedFunctions(...)overloads and usages to confirm the expected key shape. Do you want that?application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)
46-50: Filtering specialized cache names is unnecessary CacheConfigurationProperties.getAllCaches() returns only the default caches (petriNetById,petriNetByIdentifier,petriNetNewest,petriNetCache,loadedModules) and any user-added names—it never includesCacheMapKeys.ACTIONS,CacheMapKeys.FUNCTIONS, orCacheMapKeys.NAMESPACE_FUNCTIONS, so no duplicate registrations can occur by default.Likely an incorrect or invalid review comment.
...ation-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java
Outdated
Show resolved
Hide resolved
| @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)); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cache.get(Object) should return null on miss; don’t wrap null
Returning SimpleValueWrapper(null) violates the Cache contract. Return null when the key is absent or the reload doesn’t populate the key.
@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));
+ Object refreshed = map.get(stringKey);
+ return refreshed != null
+ ? new org.springframework.cache.support.SimpleValueWrapper(refreshed)
+ : null;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @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)); | |
| } | |
| @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); | |
| Object refreshed = map.get(stringKey); | |
| return refreshed != null | |
| ? new org.springframework.cache.support.SimpleValueWrapper(refreshed) | |
| : null; | |
| } |
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java
around lines 17 to 27, the get(Object) implementation currently returns new
SimpleValueWrapper(null) on misses which violates the Cache contract; modify the
method to convert the key to string, try map.get(stringKey) and if non-null
return a SimpleValueWrapper, otherwise call
fieldActionsCacheService.reloadCachedFunctions(stringKey), then re-check
map.get(stringKey) and return a SimpleValueWrapper only if that second lookup is
non-null; if still null return null (do not wrap null).
| public <T> T get(Object key, Class<T> 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)); | ||
|
|
||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Override typed get; mirror null semantics after reload
Add @Override and return null when still missing.
- public <T> T get(Object key, Class<T> type) {
+ @Override
+ public <T> T get(Object key, Class<T> 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));
+ Object refreshed = map.get(stringKey);
+ return refreshed != null ? type.cast(refreshed) : null;
}🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java
around lines 29 to 40, add the @Override annotation to the get method and change
the logic so that after calling
fieldActionsCacheService.reloadCachedFunctions(stringKey) you read the map again
into a local variable, return null if that lookup is still missing, and only
call type.cast(...) when the looked-up value is non-null to preserve the
original null semantics and avoid a ClassCastException.
...on-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java
Outdated
Show resolved
Hide resolved
| Supplier<Map<String, CachedFunction>> functionsFactory = () -> new MaxSizeHashMap<>(fieldRunnerProperties.getFunctionsCacheSize()); | ||
|
|
||
| caches.add(new FunctionsMapCache( | ||
| CacheMapKeys.FUNCTIONS, | ||
| functionsFactory, | ||
| fieldActionsCacheService, | ||
| petriNetService | ||
| )); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Align FUNCTIONS cache with stored type from FunctionsMapCache
With FunctionsMapCache storing Function, adjust the factory’s type accordingly.
- Supplier<Map<String, CachedFunction>> functionsFactory = () -> new MaxSizeHashMap<>(fieldRunnerProperties.getFunctionsCacheSize());
+ Supplier<Map<String, Function>> functionsFactory = () -> new MaxSizeHashMap<>(fieldRunnerProperties.getFunctionsCacheSize());
@@
- caches.add(new FunctionsMapCache(
+ caches.add(new FunctionsMapCache(
CacheMapKeys.FUNCTIONS,
functionsFactory,
fieldActionsCacheService,
petriNetService
));Ensure you import com.netgrif.application.engine.objects.petrinet.domain.Function.
@@
-import com.netgrif.application.engine.workflow.domain.CachedFunction;
+import com.netgrif.application.engine.objects.petrinet.domain.Function;Note: Keep the CachedFunction import for the namespace cache below.
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java
around lines 61 to 69, the Supplier factory is typed as Supplier<Map<String,
CachedFunction>> but FunctionsMapCache stores Function instances; change the
factory type to Supplier<Map<String,
com.netgrif.application.engine.objects.petrinet.domain.Function>> (and update
the MaxSizeHashMap generic accordingly) and add an import for
com.netgrif.application.engine.objects.petrinet.domain.Function; keep the
existing CachedFunction import for the namespace cache below.
| /** | ||
| * 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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Return nullability + index requirement for fast lookups by nested functions._id.
- Mark the return as nullable to match the “null if not found” contract.
- Add an index on petriNet.functions._id; without it, lookups will scan the whole collection at scale.
Apply near the signature:
- Function findByFunctionId(String functionId);
+ @org.springframework.lang.Nullable
+ Function findByFunctionId(String functionId);Create the index during startup (e.g., in configuration):
// Example (place in a @Configuration initializer)
mongoTemplate.indexOps("petriNet")
.ensureIndex(new org.springframework.data.mongodb.core.index.Index()
.on("functions._id", org.springframework.data.domain.Sort.Direction.ASC));Would you like me to open a follow-up for the index migration?
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
around lines 337 to 345, the method signature and docs promise "null if not
found" but the return isn't marked nullable and there is no note about indexing
for performance; update the method signature to indicate the return can be null
(use your codebase's Nullable annotation or JSR305/@Nullable) and update the
Javadoc accordingly, and add a startup configuration that ensures an ascending
index on petriNet.functions._id (create the index via your
MongoTemplate/IndexOps during application startup) so nested lookups are
efficient at scale.
| @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<Function> optionalFunction = petriNet.getFunctions().stream().filter(function -> function.getObjectId().toString().equals(functionId)).findFirst(); | ||
|
|
||
| return optionalFunction.orElse(null); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix NPE on miss and validate ObjectId; project only the matched function.
Current code NPEs when no PetriNet matches and accepts invalid ids. Also loads all functions; use elemMatch to project the matched one only.
Apply:
@@
@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<Function> optionalFunction = petriNet.getFunctions().stream().filter(function -> function.getObjectId().toString().equals(functionId)).findFirst();
-
- return optionalFunction.orElse(null);
+ if (functionId == null || functionId.isBlank() || !ObjectId.isValid(functionId)) {
+ throw new IllegalArgumentException("Invalid functionId: " + functionId);
+ }
+ ObjectId oid = new ObjectId(functionId);
+ Query query = new Query(Criteria.where("functions._id").is(oid));
+ // project only the matched array element to reduce payload
+ query.fields().elemMatch("functions", Criteria.where("_id").is(oid));
+
+ PetriNet petriNet = mongoTemplate.findOne(query, PetriNet.class, "petriNet");
+ if (petriNet == null || petriNet.getFunctions() == null || petriNet.getFunctions().isEmpty()) {
+ return null;
+ }
+ // after elemMatch the first item is the matched function
+ Function f = petriNet.getFunctions().getFirst();
+ return (f != null && oid.equals(f.getObjectId())) ? f : null;
}Committable suggestion skipped: line range outside the PR's diff.
| return new HashMap<>((Map) cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS).getNativeCache()); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Return a safe, typed copy of the namespace function cache.
- return new HashMap<>((Map) cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS).getNativeCache());
+ @SuppressWarnings("unchecked")
+ Map<String, List<CachedFunction>> nativeMap =
+ (Map<String, List<CachedFunction>>) getRequiredCache(CacheMapKeys.NAMESPACE_FUNCTIONS).getNativeCache();
+ return new HashMap<>(nativeMap);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return new HashMap<>((Map) cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS).getNativeCache()); | |
| } | |
| @SuppressWarnings("unchecked") | |
| Map<String, List<CachedFunction>> nativeMap = | |
| (Map<String, List<CachedFunction>>) getRequiredCache(CacheMapKeys.NAMESPACE_FUNCTIONS).getNativeCache(); | |
| return new HashMap<>(nativeMap); | |
| } |
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
around lines 147-148, the method currently returns an unchecked raw-cast of the
native cache which is unsafe and may return the underlying map directly; change
it to obtain the cache object, null-check it, cast it to the expected generic
type (e.g. Map<String, YourValueType>), and return a new typed HashMap copy (or
Collections.emptyMap() when null) to ensure type safety and avoid exposing the
native cache instance.
.../src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
Outdated
Show resolved
Hide resolved
| void reloadCachedFunctions(String petriNetId); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Document the new overload and define its contract (evict vs. rebuild).
Clarify whether this method evicts and then rebuilds, or only rebuilds. Also specify expected ID format (24-hex) and error behavior.
Apply Javadoc near the signature:
+ /**
+ * Reloads (evict + rebuild) cached functions for the given PetriNet ID.
+ * The ID must be a valid 24-hex ObjectId.
+ * @throws IllegalArgumentException if {@code petriNetId} is invalid or the PetriNet does not exist
+ */
void reloadCachedFunctions(String petriNetId);🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java
around lines 16 to 17, add Javadoc to the reloadCachedFunctions(String
petriNetId) overload that (1) states its contract clearly — whether it evicts
the existing cache entry and rebuilds it or only rebuilds in-place (choose and
document one behavior explicitly), (2) documents the expected petriNetId format
(24-character lowercase hex string) and validation behavior, (3) documents error
behavior (throw IllegalArgumentException for invalid id format,
NoSuchElementException if the referenced Petri net does not exist, and
Propagate/Log RuntimeException for rebuild failures), and (4) include any
thread-safety and performance notes (e.g., this method is blocking and may
acquire cache lock). Ensure the Javadoc is concise and placed immediately above
the method signature to define the contract unambiguously.
|
There are test errors |
| .collect(Collectors.toCollection(ArrayList::new)); | ||
|
|
||
|
|
||
| Supplier<Map<String, Closure>> actionsFactory = () -> new MaxSizeHashMap<>(fieldRunnerProperties.getActionCacheSize()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MaxSizeHashMap is not a thread safe collection. Multiple threads can read and write to the cache at the same time
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing this out, I’ll fix it.
…e mutation on get or put
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
♻️ Duplicate comments (9)
application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java (2)
21-32: Honor Cache.get contract; avoid NPE; remove unnecessary PetriNet fetch; handle contention.
- Return null on miss, not ValueWrapper(null).
- Use reload-by-id API; fetching PetriNet is unnecessary.
- Prevent thundering herd by delegating to GenericMapCache.get(key, loader).
Apply:
@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<CachedFunction>) valueObject)); - } - PetriNet petriNet = petriNetService.getPetriNet(stringKey); - fieldActionsCacheService.cachePetriNetFunctions(petriNet); - return new SimpleValueWrapper(List.copyOf((List<CachedFunction>) map().get(stringKey))); - } + public ValueWrapper get(Object key) { + if (key == null) throw new IllegalArgumentException("Cache key must not be null"); + final String stringKey = key.toString(); + List<CachedFunction> value = this.get(stringKey, () -> { + fieldActionsCacheService.reloadCachedFunctions(stringKey); + List<CachedFunction> v = (List<CachedFunction>) map().get(stringKey); + return v == null ? null : List.copyOf(v); + }); + return value == null ? null : new SimpleValueWrapper(value); + }
34-46: Override typed get; mirror null-miss semantics; avoid PetriNet fetch.Also add key validation and contention-safe loader.
Apply:
- public <T> T get(Object key, Class<T> type) { - String stringKey = String.valueOf(key); - Object valueObject = map().get(stringKey); - - if (valueObject != null) { - return type.cast(List.copyOf((List<CachedFunction>) valueObject)); - } - - PetriNet petriNet = petriNetService.getPetriNet(stringKey); - fieldActionsCacheService.cachePetriNetFunctions(petriNet); - return type.cast(List.copyOf((List<CachedFunction>) map().get(stringKey))); - - } + @Override + public <T> T get(Object key, Class<T> type) { + if (key == null) throw new IllegalArgumentException("Cache key must not be null"); + final String stringKey = key.toString(); + List<CachedFunction> value = this.get(stringKey, () -> { + fieldActionsCacheService.reloadCachedFunctions(stringKey); + List<CachedFunction> v = (List<CachedFunction>) map().get(stringKey); + return v == null ? null : List.copyOf(v); + }); + return value == null ? null : type.cast(value); + }application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java (3)
15-22: Parameterize the cache; align with stored type; add @OverRide imports not needed.Removes raw type usage and keeps type-safety.
Apply:
-@Slf4j -public class FunctionsMapCache extends GenericMapCache { +@Slf4j +public class FunctionsMapCache extends GenericMapCache<CachedFunction> { @@ - public FunctionsMapCache(String name, java.util.function.Supplier<Map<String, CachedFunction>> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService, IGroovyShellFactory shellFactory) { - super(name, CachedFunction.class, mapFactory, fieldActionsCacheService, petriNetService); + public FunctionsMapCache(String name, + java.util.function.Supplier<Map<String, CachedFunction>> mapFactory, + IFieldActionsCacheService fieldActionsCacheService, + IPetriNetService petriNetService, + IGroovyShellFactory shellFactory) { + super(name, CachedFunction.class, mapFactory, fieldActionsCacheService, petriNetService); this.shell = shellFactory.getGroovyShell(); }
25-40: Fix miss semantics; avoid double build; use super.put; validate key.
- Return null when absent.
- Build once on miss; reuse built instance for storage and response (or consistently return a copy).
Apply:
@Override public synchronized ValueWrapper get(Object key) { - String stringKey = String.valueOf(key); + if (key == null) throw new IllegalArgumentException("Cache key must not be null"); + String stringKey = key.toString(); Object valueObject = map().get(stringKey); if (valueObject != null) { - return new SimpleValueWrapper(CachedFunction.copyOf(shell, (CachedFunction) valueObject)); + 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)); + CachedFunction built = CachedFunction.build(shell, function); + super.put(stringKey, built); + return new SimpleValueWrapper(CachedFunction.copyOf(shell, built)); } - return new SimpleValueWrapper(null); + return null; }
42-57: Override typed get; unify semantics with untyped; return null on miss.Also avoid double build and write via super.put.
Apply:
- public synchronized <T> T get(Object key, Class<T> type) { - String stringKey = String.valueOf(key); + @Override + public synchronized <T> T get(Object key, Class<T> type) { + if (key == null) throw new IllegalArgumentException("Cache key must not be null"); + String stringKey = key.toString(); Object valueObject = map().get(stringKey); if (valueObject != null) { - return type.cast(valueObject); + return type.cast(CachedFunction.copyOf(shell, (CachedFunction) valueObject)); } Function function = petriNetService.findByFunctionId(stringKey); if (function != null) { - map().put(stringKey, CachedFunction.build(shell, function)); - return type.cast(CachedFunction.build(shell, function)); + CachedFunction built = CachedFunction.build(shell, function); + super.put(stringKey, built); + return type.cast(CachedFunction.copyOf(shell, built)); } - return type.cast(null); + return null; }application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (3)
12-12: Parameterize the cache with Closure to eliminate raw typesExtend
GenericMapCache<Closure>for compile-time safety.- public class ActionsMapCache extends GenericMapCache { + public class ActionsMapCache extends GenericMapCache<Closure> {
22-28: Honor Cache contract: return null on miss, don’t wrap nullWrapping
nullinSimpleValueWrapperviolates Spring’sCachecontract.Object valueObject = map().get(stringKey); if (valueObject != null) { return new SimpleValueWrapper(valueObject); } fieldActionsCacheService.reloadCachedFunctions(stringKey); - return new SimpleValueWrapper(map().get(stringKey)); + Object refreshed = map().get(stringKey); + return refreshed != null ? new SimpleValueWrapper(refreshed) : null;
30-41: Add @OverRide and mirror null semantics for typed getAlso avoid
type.cast(null)and returnnulldirectly.- public synchronized <T> T get(Object key, Class<T> type) { + @Override + public synchronized <T> T get(Object key, Class<T> 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)); + Object refreshed = map().get(stringKey); + return refreshed != null ? type.cast(refreshed) : null; }Run to locate similar violations across caches:
#!/bin/bash rg -nP --type=java 'SimpleValueWrapper\\(null\\)|return\\s+type\\.cast\\(null\\)'application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
17-18: Tighten generics: use Class and make it finalReduces unchecked casts and encodes the value type at the compiler level.
- protected Class<?> valueType; + protected final Class<V> valueType; @@ - public GenericMapCache(String name, Class<?> valueType, java.util.function.Supplier<Map<String, V>> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService) { + public GenericMapCache(String name, Class<V> valueType, java.util.function.Supplier<Map<String, V>> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService) {Also applies to: 26-26
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (6)
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/workflow/domain/CachedFunction.java(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java (4)
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (1)
Slf4j(11-42)application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java (1)
Slf4j(14-53)application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
Slf4j(14-104)application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
Slf4j(66-581)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (4)
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (1)
Slf4j(11-42)application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java (1)
Slf4j(14-58)application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java (1)
Slf4j(14-53)application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
Slf4j(66-581)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
CacheMapKeys(3-7)
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (4)
application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java (1)
Slf4j(14-58)application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java (1)
Slf4j(14-53)application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
Slf4j(14-104)application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
Slf4j(66-581)
application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java (4)
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (1)
Slf4j(11-42)application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java (1)
Slf4j(14-58)application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
Slf4j(14-104)application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
Slf4j(66-581)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: Test
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
🔇 Additional comments (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)
53-82: Good: bounded maps wrapped with synchronizationWrapping
MaxSizeHashMapwithCollections.synchronizedMapaddresses immediate thread-safety concerns for shared caches.
| @Override | ||
| public synchronized ValueWrapper get(Object key) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix return type reference: unresolved ValueWrapper
ValueWrapper is an inner type of org.springframework.cache.Cache; reference it properly to fix compilation.
- public synchronized ValueWrapper get(Object key) {
+ public synchronized org.springframework.cache.Cache.ValueWrapper get(Object key) {Add this import outside the shown range (either one of the two):
- import org.springframework.cache.Cache;
- or import org.springframework.cache.Cache.ValueWrapper;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Override | |
| public synchronized ValueWrapper get(Object key) { | |
| @Override | |
| public synchronized org.springframework.cache.Cache.ValueWrapper get(Object key) { |
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java
around lines 18-19, the method signature uses ValueWrapper but the type is
unresolved because ValueWrapper is an inner type of
org.springframework.cache.Cache; fix by adding an import for the inner type
(import org.springframework.cache.Cache.ValueWrapper;) or import the outer type
(import org.springframework.cache.Cache;) and reference the type explicitly as
Cache.ValueWrapper in the signature; add the chosen import at the top of the
file (outside the shown range) so the method compiles.
...on-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java
Show resolved
Hide resolved
| Supplier<Map<String, Closure>> actionsFactory = | ||
| () -> Collections.synchronizedMap(new MaxSizeHashMap<>(fieldRunnerProperties.getActionCacheSize())); | ||
|
|
||
| caches.add(new ActionsMapCache( | ||
| CacheMapKeys.ACTIONS, | ||
| actionsFactory, | ||
| fieldActionsCacheService, | ||
| petriNetService | ||
| )); | ||
|
|
||
| Supplier<Map<String, CachedFunction>> functionsFactory = | ||
| () -> Collections.synchronizedMap(new MaxSizeHashMap<>(fieldRunnerProperties.getFunctionsCacheSize())); | ||
|
|
||
| caches.add(new FunctionsMapCache( | ||
| CacheMapKeys.FUNCTIONS, | ||
| functionsFactory, | ||
| fieldActionsCacheService, | ||
| petriNetService, | ||
| groovyShellFactory | ||
| )); | ||
|
|
||
| Supplier<Map<String, List<CachedFunction>>> nsFactory = | ||
| () -> Collections.synchronizedMap(new MaxSizeHashMap<>(fieldRunnerProperties.getNamespaceCacheSize())); | ||
|
|
||
| caches.add(new FunctionsNamespaceMapCache( | ||
| CacheMapKeys.NAMESPACE_FUNCTIONS, | ||
| nsFactory, | ||
| fieldActionsCacheService, | ||
| petriNetService | ||
| )); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Consider Caffeine for production-grade bounded caches
Caffeine gives O(1) concurrency, admission/eviction policies, and metrics; wire CaffeineCache instances into SimpleCacheManager as a follow-up.
| Object valueObject = map().get(stringKey); | ||
| if (valueObject != null) { | ||
| return new SimpleValueWrapper(CachedFunction.copyOf(shell, (CachedFunction) valueObject)); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Consider eliminating per-call copyOf for performance.
Re-evaluating Groovy on every hit undermines caching. If thread-safety allows, return the stored CachedFunction directly, or cache compiled classes and rehydrate closures cheaply. If you must copy, consider Closure#rehydrate over full re-eval.
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java
around lines 28-31, the code currently calls CachedFunction.copyOf(...) on every
cache hit which forces Groovy re-evaluation and kills performance; instead, if
CachedFunction is immutable or can be made thread-safe, return the stored
CachedFunction directly from the cache; otherwise change the cache to store a
compiled/rehydratable Closure or class and create lightweight per-call wrappers
via Closure.rehydrate (or a cached compiled form) rather than full re-eval, or
implement copy-on-write so copies are made only on mutating calls — pick one
approach, make CachedFunction immutable or mark it thread-safe, update the cache
return to return the stored instance when safe, or update the cache value to a
compiled/rehydratable closure and create lightweight closures on read.
| public class FunctionsNamespaceMapCache extends GenericMapCache { | ||
|
|
||
| public FunctionsNamespaceMapCache(String name, java.util.function.Supplier<Map<String, List<CachedFunction>>> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService) { | ||
| super(name, List.class, mapFactory, fieldActionsCacheService, petriNetService); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Parameterize the cache type; avoid raw GenericMapCache.
Prevents unsafe casts and lets GenericMapCache.safeCast help.
Apply:
-@Slf4j
-public class FunctionsNamespaceMapCache extends GenericMapCache {
+@Slf4j
+public class FunctionsNamespaceMapCache extends GenericMapCache<List<CachedFunction>> {
@@
- public FunctionsNamespaceMapCache(String name, java.util.function.Supplier<Map<String, List<CachedFunction>>> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService) {
- super(name, List.class, mapFactory, fieldActionsCacheService, petriNetService);
- }
+ public FunctionsNamespaceMapCache(String name,
+ java.util.function.Supplier<Map<String, List<CachedFunction>>> mapFactory,
+ IFieldActionsCacheService fieldActionsCacheService,
+ IPetriNetService petriNetService) {
+ super(name, List.class, mapFactory, fieldActionsCacheService, petriNetService);
+ }🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java
around lines 15–19, the class currently extends the raw GenericMapCache which
causes unsafe casts; change the class to extend GenericMapCache<Map<String,
List<CachedFunction>>> and update the constructor signature to accept
Supplier<Map<String, List<CachedFunction>>>; in the super(...) call replace the
raw List.class with a properly typed Class for Map (use
GenericMapCache.safeCast(Map.class) or an equivalent safeCast to produce a
Class<Map<String, List<CachedFunction>>>), ensuring all generics line up so no
raw types remain and unsafe casting is avoided.
| @Override | ||
| public void put(Object key, Object value) { | ||
| String k = String.valueOf(key); | ||
| map().put(k, List.copyOf((List<CachedFunction>) value)); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Use super.put and enforce immutability once.
Avoid bypassing safeCast; still store an unmodifiable List.
Apply:
@Override
public void put(Object key, Object value) {
- String k = String.valueOf(key);
- map().put(k, List.copyOf((List<CachedFunction>) value));
+ if (key == null) throw new IllegalArgumentException("Cache key must not be null");
+ super.put(key, List.copyOf((List<CachedFunction>) value));
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Override | |
| public void put(Object key, Object value) { | |
| String k = String.valueOf(key); | |
| map().put(k, List.copyOf((List<CachedFunction>) value)); | |
| } | |
| @Override | |
| public void put(Object key, Object value) { | |
| if (key == null) throw new IllegalArgumentException("Cache key must not be null"); | |
| super.put(key, List.copyOf((List<CachedFunction>) value)); | |
| } |
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java
around lines 48-52, the current implementation bypasses safeCast and writes
directly to map(); change it to call super.put(key, value) and ensure
immutability exactly once: use the class's safeCast (or equivalent) to cast the
incoming value to List<CachedFunction>, wrap that result with List.copyOf(...)
to make it unmodifiable, and pass that wrapped list into super.put(key,
wrappedList) instead of map().put(...).
...ation-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java
Outdated
Show resolved
Hide resolved
...ation-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java
Show resolved
Hide resolved
| public static CachedFunction copyOf(GroovyShell shell, CachedFunction cachedFunction) { | ||
| if (cachedFunction == null) { | ||
| return null; | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Guard against null input early (returning null here may mask bugs).
Prefer failing fast on null input or document the nullable contract. I recommend throwing IllegalArgumentException for null cachedFunction to avoid silent nulls propagating.
Apply:
- public static CachedFunction copyOf(GroovyShell shell, CachedFunction cachedFunction) {
- if (cachedFunction == null) {
- return null;
- }
+ public static CachedFunction copyOf(GroovyShell shell, CachedFunction cachedFunction) {
+ if (cachedFunction == null) {
+ throw new IllegalArgumentException("cachedFunction must not be null");
+ }🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/workflow/domain/CachedFunction.java
around lines 27 to 31, the copyOf method currently returns null when
cachedFunction is null which can hide errors; change the method to fail fast by
throwing an IllegalArgumentException when cachedFunction is null (with a clear
message like "cachedFunction must not be null") instead of returning null so
callers immediately see the contract violation.
| Closure code = (Closure) shell.evaluate(cachedFunction.getFunction().getDefinition()); | ||
| if (code == null) { | ||
| throw new IllegalArgumentException("Non compilable function"); | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Harden evaluation: null/blank definition, non-Closure result, and exceptions.
Current code may NPE on null definition and ClassCastException on non-Closure. Wrap compilation errors consistently.
Apply:
- Closure code = (Closure) shell.evaluate(cachedFunction.getFunction().getDefinition());
- if (code == null) {
- throw new IllegalArgumentException("Non compilable function");
- }
+ String def = cachedFunction.getFunction() != null ? cachedFunction.getFunction().getDefinition() : null;
+ if (def == null || def.isBlank()) {
+ throw new IllegalArgumentException("Function definition must not be null/blank");
+ }
+ final Object evaluated;
+ try {
+ evaluated = shell.evaluate(def);
+ } catch (Throwable t) {
+ throw new IllegalArgumentException("Non compilable function", t);
+ }
+ if (!(evaluated instanceof Closure)) {
+ throw new IllegalArgumentException("Function definition did not evaluate to a Closure");
+ }
+ Closure code = (Closure) evaluated;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Closure code = (Closure) shell.evaluate(cachedFunction.getFunction().getDefinition()); | |
| if (code == null) { | |
| throw new IllegalArgumentException("Non compilable function"); | |
| } | |
| String def = cachedFunction.getFunction() != null | |
| ? cachedFunction.getFunction().getDefinition() | |
| : null; | |
| if (def == null || def.isBlank()) { | |
| throw new IllegalArgumentException("Function definition must not be null/blank"); | |
| } | |
| final Object evaluated; | |
| try { | |
| evaluated = shell.evaluate(def); | |
| } catch (Throwable t) { | |
| throw new IllegalArgumentException("Non compilable function", t); | |
| } | |
| if (!(evaluated instanceof Closure)) { | |
| throw new IllegalArgumentException("Function definition did not evaluate to a Closure"); | |
| } | |
| Closure code = (Closure) evaluated; |
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/workflow/domain/CachedFunction.java
around lines 32 to 36, harden the evaluation: first validate
cachedFunction.getFunction().getDefinition() for null or blank and throw
IllegalArgumentException with a clear message if invalid; then call
shell.evaluate(...) inside a try/catch to catch and wrap any compilation/runtime
exceptions into an IllegalArgumentException that includes the original
exception; after evaluation verify the returned object is a Closure (not null or
another type) and if not throw IllegalArgumentException indicating non-Closure
result (including actual result type when available).
…namespaceFunctionsCache from local Map to Redis
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
♻️ Duplicate comments (8)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
59-61: Fix compile error: qualify ValueRetrievalExceptionIt’s an inner type of Cache.
- } catch (Exception ex) { - throw new ValueRetrievalException(stringKey, loader, ex); + } catch (Exception ex) { + throw new Cache.ValueRetrievalException(stringKey, loader, ex); }application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (6)
58-65: Fail fast on missing namespace functions cacheUse a required-cache helper to avoid NPEs and produce clear errors.
- Cache namespaceFunctionsCache = cacheManager.getCache(properties.getNamespaceFunctions()); + Cache namespaceFunctionsCache = getRequiredCache(properties.getNamespaceFunctions());
149-151: Return a typed copy of the namespace cache; avoid raw castsSafer and future‑proof.
- return new HashMap<>((Map) cacheManager.getCache(properties.getNamespaceFunctions()).getNativeCache()); + @SuppressWarnings("unchecked") + Map<String, List<CachedFunction>> nativeMap = + (Map<String, List<CachedFunction>>) getRequiredCache(properties.getNamespaceFunctions()).getNativeCache(); + return new HashMap<>(nativeMap);
154-166: Clear caches via required-cache helperPrevents NPEs if a cache is missing.
- cacheManager.getCache(CacheMapKeys.ACTIONS).clear(); + getRequiredCache(CacheMapKeys.ACTIONS).clear(); @@ - cacheManager.getCache(properties.getNamespaceFunctions()).clear(); + getRequiredCache(properties.getNamespaceFunctions()).clear(); @@ - cacheManager.getCache(CacheMapKeys.FUNCTIONS).clear(); + getRequiredCache(CacheMapKeys.FUNCTIONS).clear();
69-72: Guard reload path with required-cache helperPrevents NPE when cache is misconfigured.
- cacheManager.getCache(properties.getNamespaceFunctions()).evictIfPresent(petriNetId); + getRequiredCache(properties.getNamespaceFunctions()).evictIfPresent(petriNetId); cachePetriNetFunctions(petriNetService.getNewestVersionByIdentifier(petriNetId));
80-92: Use atomic Cache#get(key, loader) and evict on rewrite; stop peeking into native mapThis fixes potential NPE and races, and honors rewrite semantics.
- 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 (Closure) actionsCache.get(stringId).get(); + Cache actionsCache = getRequiredCache(CacheMapKeys.ACTIONS); + if (shouldRewriteCachedActions) { + actionsCache.evict(stringId); + } + return actionsCache.get(stringId, () -> (Closure) shell.evaluate("{-> " + action.getDefinition() + "}"));
95-107: Compute functions via Cache API, no native map castingCleaner, thread-safe, and backend-agnostic.
- 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()); - }); - } + Cache functionsCache = getRequiredCache(CacheMapKeys.FUNCTIONS); + for (Function function : functions) { + CachedFunction cf = functionsCache.get(function.getStringId(), + () -> CachedFunction.build(shell, function)); + cachedFunctions.add(cf); + }application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)
38-42: Avoid duplicate registrations for specialized cachesFilter out ACTIONS and FUNCTIONS from the generic cache list before adding specialized implementations.
- Set<String> cacheNames = properties.getAllCaches(); - List<Cache> caches = cacheNames.stream() + Set<String> cacheNames = new LinkedHashSet<>(properties.getAllCaches()); + cacheNames.removeAll(Set.of(CacheMapKeys.ACTIONS, CacheMapKeys.FUNCTIONS)); + List<Cache> caches = cacheNames.stream() .map(ConcurrentMapCache::new) .collect(Collectors.toCollection(ArrayList::new));
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (5)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java(2 hunks)application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java(4 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
CacheMapKeys(3-6)
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (3)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
CacheMapKeys(3-6)application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
Slf4j(12-122)application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
Slf4j(66-581)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: task-list-completed
- GitHub Check: Test
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
🔇 Additional comments (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java (1)
44-48: LGTM on adding namespaceFunctions property and wiring it into getAllCaches()Also applies to: 62-63
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java
Outdated
Show resolved
Hide resolved
...ation-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java
Outdated
Show resolved
Hide resolved
...ation-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java
Show resolved
Hide resolved
.../src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
Show resolved
Hide resolved
…e to changes in cache structure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (4)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java(2 hunks)application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-31T23:40:46.499Z
Learnt from: tuplle
PR: netgrif/application-engine#334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.
Applied to files:
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.javaapplication-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: Build
🔇 Additional comments (4)
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (2)
567-567: No functional change here.Whitespace-only change; nothing to address.
8-8: Cache names match — no action required.
@Cacheable values in PetriNetService ("petriNetCache" — line 158; "petriNetById" — 250; "petriNetByIdentifier" — 261; "petriNetNewest" — 284) exactly match defaults in CacheConfigurationProperties (lines 22, 27, 32, 37).application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
3-7: LGTM — constants holder is correctly non-instantiable.
Matches prior guidance: final class + private ctor; fields are public static final. No issues.application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
33-39: No action required — factories already return synchronized maps. CacheConfiguration creates GenericMapCache instances with Suppliers that wrap MaxSizeHashMap via Collections.synchronizedMap (application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java:45–58), so the current unsynchronized reads are safe.
| @Override public Object getNativeCache() { return Map.copyOf(map()); } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick
Preserve native cache identity; clear in place.
Returning Map.copyOf breaks “native” semantics and reassigning the map on clear invalidates existing nativeCache holders. Return the backing map and clear it in place.
Apply this diff:
- @Override public Object getNativeCache() { return Map.copyOf(map()); }
+ @Override public Object getNativeCache() { return map(); }
@@
- public synchronized void clear() {
- this.atomicMapRef.set(mapFactory.get());
- }
+ public synchronized void clear() {
+ map().clear();
+ log.debug("{} cache cleared", name);
+ }Also applies to: 95-98
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java
around lines 30-31 (and likewise adjust lines 95-98), the getNativeCache method
currently returns Map.copyOf(map()) which breaks native cache identity; change
getNativeCache to return the backing map directly (return map()) so callers keep
the same instance. Also modify clear logic (lines ~95-98) to clear the existing
backing map in place (call map().clear()) instead of reassigning a new map
instance, preserving nativeCache holders and expected semantics.
| @Override | ||
| public synchronized ValueWrapper get(Object key) { | ||
| String stringKey = String.valueOf(key); | ||
| Object valueObject = map().get(stringKey); | ||
| return valueObject != null ? new SimpleValueWrapper(valueObject) : null; | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick
Reduce contention after ensuring thread-safe map.
Once the backing map is guaranteed thread-safe (ConcurrentMap or synchronizedMap), method-level synchronized on reads/writes becomes redundant and harms throughput. Consider removing synchronized from get(key), get(key, Class), put, evict.
Also applies to: 72-83, 85-93
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java
around lines 65-71 (and similarly for 72-83 and 85-93), the methods are declared
synchronized which is redundant if the backing map is a thread-safe
ConcurrentMap or synchronizedMap; remove the synchronized modifier from
get(Object), get(Object, Class), put(...) and evict(...) methods, ensure the
map() method returns a thread-safe map implementation (e.g., ConcurrentHashMap
or Collections.synchronizedMap(...)) and add a short comment/javadoc asserting
the map is thread-safe so callers know method-level synchronization was
intentionally removed.
...ation-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java
Show resolved
Hide resolved
| 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.*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick
Avoid wildcard imports; restore explicit imports.
Wildcard imports are against common style guides (and often blocked by Checkstyle), can hide accidental type collisions, and make IDE organize‑imports unstable. Please revert to explicit imports for the actually used types (PetriNet, PetriNetSearch, Transition, VersionType).
Apply:
-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;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 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; |
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
around line 4, replace the wildcard import
"com.netgrif.application.engine.objects.petrinet.domain.*" with explicit imports
for the actual types used (PetriNet, PetriNetSearch, Transition, VersionType) so
the file imports only those classes; update the import statements accordingly
and run organizer/IDE fix imports to ensure no other unused imports remain.
...on-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
Outdated
Show resolved
Hide resolved
renczesstefan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please review coderabbitAI comments.
| private final CacheConfigurationProperties properties; | ||
|
|
||
| public CacheConfiguration(CacheConfigurationProperties properties) { | ||
| public CacheConfiguration(RunnerConfigurationProperties.FieldRunnerProperties fieldRunnerProperties, CacheConfigurationProperties properties) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its only cosmetic, but instead of this constructor you can use @RequiredArgsConstructor over the class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you. I will fix that.
| public void reloadCachedFunctions(PetriNet petriNet) { | ||
| namespaceFunctionsCache.remove(petriNet.getIdentifier()); | ||
| cachePetriNetFunctions(petriNetService.getNewestVersionByIdentifier(petriNet.getIdentifier())); | ||
| this.reloadCachedFunctions(petriNet.getIdentifier()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this be always valid? Is it possible that there will be petri net with multiple valid versions? I am thinking about the situation, where there is no migration executed for newest version, and the old cases are using old petri nets.
- fix repository query - fix null pointer exception - fix unwanted java reference
- fix version initialization
- implement tests to test the version
- implement API to activate the process
- fix logging
- fix log message
- remove unused PetriNetService.evaluateRules method - remove the explicit rollback when saving process changes -> resolved in EE as transactions
- remove todos - implement PetriNetServiceTest.testMakeVersionActive
- split PetriNetService.save into two methods
- rename version active attribute in PetriNet
- remove makeVersionActive API
- update PetriNetService.deletePetriNet to handle versionActive attribute - implement another test for process deletion
- mark methods import petriNet method transactional
- update javadoc
- add null checks
- add null check
- update javadoc
- add null check
- 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
…2212_w_2266 # Conflicts: # application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java # application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
- renamed function in IFieldActionsCacheService according to PR
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.
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()`.
# Conflicts: # application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java # application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java # application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
…tions. 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/domain/PetriNet.java (1)
63-107: Consider removing commented-out code or creating a tracked issue.The TODO comment on already commented-out code (44 lines) is a code smell. If the clone method is not needed, remove it now rather than deferring the decision. If there's genuine uncertainty, create a proper JIRA ticket to track the evaluation.
Would you like me to help evaluate whether this clone method is used anywhere in the codebase, or would you prefer to remove it immediately and rely on the superclass implementation?
🔎 If the clone method is confirmed as unused, apply this diff to remove the dead code:
- // todo: delete clone method if not needed -// @Override -// public PetriNet clone() { -// PetriNet clone = new PetriNet(); -// clone.setIdentifier(this.getIdentifier()); -// clone.setUriNodeId(this.getUriNodeId()); -// clone.setInitials(this.getInitials()); -// clone.setTitle(this.getTitle().clone()); -// clone.setDefaultRoleEnabled(this.isDefaultRoleEnabled()); -// clone.setDefaultCaseName(this.getDefaultCaseName() == null ? null : this.getDefaultCaseName().clone()); -// clone.setDefaultCaseNameExpression(this.getDefaultCaseNameExpression() == null ? null : this.getDefaultCaseNameExpression().clone()); -// clone.setIcon(this.getIcon()); -// clone.setCreationDate(this.getCreationDate()); -// clone.setVersion(this.getVersion() == null ? null : this.getVersion().clone()); -// clone.setAuthor(this.getAuthor() == null ? null : this.getAuthor().clone()); -// clone.setTransitions(this.getTransitions() == null ? null : this.getTransitions().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> new com.netgrif.application.engine.adapter.spring.petrinet.domain.Transition(e.getValue()), (v1, v2) -> v1, LinkedHashMap::new))); -// clone.setRoles(this.roles == null ? null : this.roles.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> new com.netgrif.application.engine.adapter.spring.petrinet.domain.roles.ProcessRole(e.getValue()), (v1, v2) -> v1, LinkedHashMap::new))); -// clone.setTransactions(this.getTransactions() == null ? null : this.getTransactions().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> new com.netgrif.application.engine.adapter.spring.petrinet.domain.Transaction(e.getValue()), (v1, v2) -> v1, LinkedHashMap::new))); -// clone.setImportXmlPath(this.getImportXmlPath()); -// clone.setImportId(this.importId); -// clone.setObjectId(this._id); -// clone.setDataSet(this.getDataSet().entrySet() -// .stream() -// .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().clone(), (x, y)->y, LinkedHashMap::new)) -// ); -// clone.setPlaces(this.getPlaces().entrySet() -// .stream() -// .collect(Collectors.toMap(Map.Entry::getKey, e -> new Place(e.getValue()), (x, y)->y, LinkedHashMap::new)) -// ); -// clone.setArcs(this.getArcs().entrySet() -// .stream() -// .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream() -// .map(Arc::clone) -// .collect(Collectors.toList()), (x,y)->y, LinkedHashMap::new)) -// ); -// clone.initializeArcs(); -// clone.setCaseEvents(this.getCaseEvents() == null ? null : this.getCaseEvents().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().clone()))); -// clone.setProcessEvents(this.getProcessEvents() == null ? null : this.getProcessEvents().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().clone()))); -// clone.setPermissions(this.getPermissions() == null ? null : this.getPermissions().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> new HashMap<>(e.getValue())))); -// clone.setUserRefs(this.getUserRefs() == null ? null : this.getUserRefs().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> new HashMap<>(e.getValue())))); -// this.getNegativeViewRoles().forEach(clone::addNegativeViewRole); -// this.getFunctions().forEach(clone::addFunction); -// clone.setTags(new HashMap<>(this.getTags())); -// return clone; -// }
♻️ Duplicate comments (8)
application-engine/src/main/resources/application.yaml (1)
119-121: Critical security risk: All actuator endpoints publicly accessible.Line 121 exposes all actuator endpoints (
include: "*"), and line 89 permits anonymous access to/manage/**. This combination makes sensitive endpoints (env,heapdump,beans,caches, etc.) publicly accessible without authentication.This concern was previously raised and includes recommended fixes to restrict exposure to only required endpoints and remove or secure
/manage/**from anonymous access.application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java (1)
4-4: Avoid wildcard imports; restore explicit imports.Wildcard imports violate common style guidelines, can hide accidental type collisions, and make IDE organize-imports unstable. Please use explicit imports for the actually used types (PetriNet, PetriNetSearch, Transition, VersionType).
🔎 Apply this diff:
-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;Based on past review comments, wildcard imports should be avoided in this codebase.
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java (1)
16-17: Add Javadoc for the newreloadCachedFunctions(String petriNetId)overload.This method lacks documentation specifying its contract, expected ID format, and error behavior. As noted in a previous review, clarify whether it evicts and rebuilds or only rebuilds, and document the expected format and error handling.
📝 Apply suggested Javadoc
+ /** + * Reloads (evicts and rebuilds) cached functions for the PetriNet with the given ID. + * + * @param petriNetId the MongoDB ObjectId (24-character hex string) of the PetriNet + * @throws IllegalArgumentException if the ID format is invalid or the PetriNet does not exist + */ void reloadCachedFunctions(String petriNetId);application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (2)
90-93:clear()replaces the map reference instead of clearing in-place.This approach has trade-offs:
- Pro: Ongoing operations on the old map complete without interference
- Con: Any code holding a reference to
getNativeCache()(if it returned the backing map) would have a stale referenceSince
getNativeCache()returns a copy, this is less problematic, but it's inconsistent with typical cache semantics where clearing affects the same backing store.
31-31:getNativeCache()returns a defensive copy, breaking the Spring Cache contract.The Spring Cache contract expects
getNativeCache()to return the actual backing store. ReturningMap.copyOf()means callers (likeFieldActionsCacheService.getGlobalFunctionsCache()) receive a snapshot that doesn't reflect subsequent changes, and theinstanceof Mapchecks ingetCompiledAction/getCachedFunctionsoperate on a detached copy.If defensive copying is intentional for safety, document this deviation. Otherwise, return the backing map directly.
🔎 Suggested fix:
- @Override public @NotNull Object getNativeCache() { return Map.copyOf(map()); } + @Override public @NotNull Object getNativeCache() { return map(); }application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (2)
97-112: Same TOCTOU race and NPE risk ingetCachedFunctions.Similar to
getCompiledAction, this method checkscontainsKeyon a snapshot (Map.copyOf) but operates on the actual cache. UseCache#get(key, Callable)for atomic operations:🔎 Suggested fix:
@Override public List<CachedFunction> getCachedFunctions(List<Function> functions) { List<CachedFunction> cachedFunctions = new ArrayList<>(functions.size()); Cache functionsCache = getRequiredCache(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()); - }); - } + for (Function function : functions) { + CachedFunction cf = functionsCache.get(function.getStringId(), + () -> CachedFunction.build(shell, function)); + cachedFunctions.add(cf); + } return cachedFunctions; }
82-95: TOCTOU race and potential NPE ingetCompiledAction.Two issues:
TOCTOU race:
getNativeCache()returnsMap.copyOf()(a snapshot), so!map.containsKey(stringId)checks a stale copy whileactionsCache.put()writes to the actual cache. Under concurrent access, multiple threads may redundantly compile the same action.Potential NPE at line 94: If the
instanceofcheck fails (cache impl changed), the method falls through without populating the cache, thenactionsCache.get(stringId).get()returnsnull.get().Use the
Cache#get(key, Callable)API for atomic compute-if-absent:🔎 Suggested fix:
@Override public Closure getCompiledAction(Action action, boolean shouldRewriteCachedActions) { String stringId = action.getId().toString(); Cache actionsCache = getRequiredCache(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); - } + if (shouldRewriteCachedActions) { + actionsCache.evict(stringId); } - return (Closure) actionsCache.get(stringId).get(); + return actionsCache.get(stringId, () -> (Closure) shell.evaluate("{-> " + action.getDefinition() + "}")); }application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)
36-62: Potential duplicate cache registration ifadditionalcaches include "actions" or "functions".The code creates
ConcurrentMapCacheinstances for all names fromproperties.getAllCaches(), then addsGenericMapCacheinstances forCacheMapKeys.ACTIONSandCacheMapKeys.FUNCTIONS. If a user configuresadditionalcaches containing "actions" or "functions",SimpleCacheManagerwill have duplicate cache names with different implementations.Consider filtering out the specialized cache names before mapping:
🔎 Suggested fix:
public CacheManager cacheManager() { Set<String> cacheNames = properties.getAllCaches(); + cacheNames.remove(CacheMapKeys.ACTIONS); + cacheNames.remove(CacheMapKeys.FUNCTIONS); List<Cache> caches = cacheNames.stream() .map(ConcurrentMapCache::new) .collect(Collectors.toCollection(ArrayList::new));
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (44)
application-engine/pom.xml(0 hunks)application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy(1 hunks)application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/FieldActionsRunner.groovy(1 hunks)application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy(2 hunks)application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy(2 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java(3 hunks)application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/RunnerConfigurationProperties.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/importer/service/FunctionFactory.java(3 hunks)application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java(2 hunks)application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java(2 hunks)application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java(8 hunks)application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java(2 hunks)application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/startup/runner/ImpersonationRunner.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java(4 hunks)application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java(1 hunks)application-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java(2 hunks)application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java(3 hunks)application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java(1 hunks)application-engine/src/main/resources/application.yaml(1 hunks)application-engine/src/test/groovy/com/netgrif/application/engine/TestHelper.groovy(1 hunks)application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy(2 hunks)application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy(1 hunks)application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovy(2 hunks)application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy(1 hunks)application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy(1 hunks)application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy(5 hunks)application-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy(1 hunks)application-engine/src/test/resources/change_caseref_value_action_test.xml(2 hunks)application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xml(1 hunks)application-engine/src/test/resources/petriNets/process_version_1_0_0.xml(1 hunks)application-engine/src/test/resources/petriNets/process_version_2_0_0.xml(1 hunks)application-engine/src/test/resources/petriNets/process_version_3_0_0.xml(1 hunks)application-engine/src/test/resources/petriNets/process_version_4_0_0.xml(1 hunks)application-engine/src/test/resources/petriNets/process_version_5_0_0.xml(1 hunks)nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java(3 hunks)nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/FunctionScope.java(1 hunks)nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java(6 hunks)nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/version/Version.java(2 hunks)nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/domain/PetriNet.java(1 hunks)
💤 Files with no reviewable changes (1)
- application-engine/pom.xml
🧰 Additional context used
🧠 Learnings (11)
📚 Learning: 2025-07-31T23:40:46.499Z
Learnt from: tuplle
Repo: netgrif/application-engine PR: 334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.
Applied to files:
application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.javaapplication-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.javaapplication-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xmlapplication-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovyapplication-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.javaapplication-engine/src/test/resources/petriNets/process_version_5_0_0.xmlapplication-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.javaapplication-engine/src/test/resources/petriNets/process_version_1_0_0.xmlapplication-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.javaapplication-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovynae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/domain/PetriNet.javaapplication-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.javaapplication-engine/src/main/java/com/netgrif/application/engine/startup/runner/ImpersonationRunner.javaapplication-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovynae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.javaapplication-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.javaapplication-engine/src/test/resources/petriNets/process_version_2_0_0.xmlapplication-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.javaapplication-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovyapplication-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.javaapplication-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.javaapplication-engine/src/test/resources/petriNets/process_version_3_0_0.xmlapplication-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovynae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.javaapplication-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovyapplication-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.javaapplication-engine/src/test/resources/change_caseref_value_action_test.xmlapplication-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy
📚 Learning: 2025-12-12T13:40:00.786Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 397
File: application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java:98-104
Timestamp: 2025-12-12T13:40:00.786Z
Learning: In application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java, the permission constraint logic in buildQuery() intentionally applies negative actor constraints globally: ((roleConstraint AND NOT negRoleConstraint) OR actorConstraint) AND NOT negActorConstraint. This means users in negativeViewActors are excluded even if they have positive role permissions, which is the intended authorization behavior.
Applied to files:
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.javaapplication-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.javaapplication-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.javaapplication-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-12-12T12:36:06.651Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 397
File: application-engine/src/main/resources/petriNets/engine-processes/org_group.xml:36-39
Timestamp: 2025-12-12T12:36:06.651Z
Learning: In the org_group.xml file (application-engine/src/main/resources/petriNets/engine-processes/org_group.xml), variable names in action blocks like userField and user_selection should be kept as-is even when the field type changes from "user" to "actor".
Applied to files:
application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xmlapplication-engine/src/test/resources/petriNets/process_version_5_0_0.xmlapplication-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.javaapplication-engine/src/test/resources/petriNets/process_version_4_0_0.xmlapplication-engine/src/test/resources/petriNets/process_version_1_0_0.xmlapplication-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovyapplication-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.javaapplication-engine/src/test/resources/petriNets/process_version_2_0_0.xmlapplication-engine/src/test/resources/petriNets/process_version_3_0_0.xmlapplication-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.javaapplication-engine/src/test/resources/change_caseref_value_action_test.xmlapplication-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy
📚 Learning: 2025-09-05T10:21:54.893Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 350
File: application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy:135-135
Timestamp: 2025-09-05T10:21:54.893Z
Learning: In ExportServiceTest.groovy, writing to src/test/resources is intentional to simulate production behavior where the working tree is mutated during file exports. This mirrors how the system works in production.
Applied to files:
application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xmlapplication-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-12-12T12:37:58.852Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 397
File: application-engine/src/test/groovy/com/netgrif/application/engine/auth/WorkflowAuthorizationServiceTest.groovy:4-4
Timestamp: 2025-12-12T12:37:58.852Z
Learning: In tests under application-engine/src/test, GroupService.getDefaultSystemGroup() lazily creates the default system group if it doesn't exist. Tests can rely on this behavior and omit explicit existence/setup checks for the default system group.
Applied to files:
application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/TestHelper.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy
📚 Learning: 2025-12-12T12:40:25.863Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 397
File: application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy:18-20
Timestamp: 2025-12-12T12:40:25.863Z
Learning: In Groovy/Java, if a parameter or collection expects ActorFieldValue, you can pass UserFieldValue (a subclass) due to polymorphism. In tests, verify that ActorListFieldValue accepts ActorFieldValue instances and that passing UserFieldValue instances is valid without casts. This pattern applies to similar Groovy files across the repository.
Applied to files:
application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovyapplication-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovyapplication-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/TestHelper.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovyapplication-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovyapplication-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovyapplication-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/FieldActionsRunner.groovy
📚 Learning: 2025-09-29T10:31:57.325Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 362
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/ProcessRoleService.java:513-529
Timestamp: 2025-09-29T10:31:57.325Z
Learning: PetriNet.getStringId() returns a simple ObjectId string representation (_id.toString()), not a composite Netgrif ID format, so new ObjectId(petriNetId) works correctly when petriNetId comes from PetriNet.getStringId().
Applied to files:
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.javaapplication-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.javaapplication-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.javaapplication-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovyapplication-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.javaapplication-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
📚 Learning: 2025-09-29T10:31:31.469Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 362
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/ProcessRoleController.java:43-45
Timestamp: 2025-09-29T10:31:31.469Z
Learning: In the Netgrif application engine, when Authentication objects reach controller endpoints, auth.getPrincipal() always returns a LoggedUser instance and is never null, so defensive casting checks are not necessary in controller methods.
Applied to files:
application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.javaapplication-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
📚 Learning: 2025-11-14T10:22:01.634Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 383
File: application-engine/src/main/java/com/netgrif/application/engine/startup/ApplicationRunnerOrderResolver.java:43-43
Timestamp: 2025-11-14T10:22:01.634Z
Learning: For the netgrif/application-engine repository, avoid flagging trivial or nitpick-level issues such as redundant null checks, minor code style improvements, or obvious simplifications that don't affect functionality or introduce bugs. Focus review comments on substantive issues like logic errors, security concerns, performance problems, or breaking changes.
Applied to files:
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.javaapplication-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
📚 Learning: 2025-07-29T17:19:18.300Z
Learnt from: tuplle
Repo: netgrif/application-engine PR: 331
File: application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java:45-46
Timestamp: 2025-07-29T17:19:18.300Z
Learning: In ElasticPetriNetService class, petriNetService is properly initialized using Lazy setter injection rather than constructor injection. This pattern with Lazy Autowired setter methods is commonly used in Spring to resolve circular dependencies and is a valid alternative to constructor injection.
Applied to files:
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/domain/PetriNet.javanae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.javaapplication-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovyapplication-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
📚 Learning: 2025-06-23T13:30:13.096Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 318
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemConstants.java:60-62
Timestamp: 2025-06-23T13:30:13.096Z
Learning: In MenuItemConstants enum in nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemConstants.java, the field `attributeId` will be renamed to `value` to make it more generic and appropriate for both dataset attribute identifiers and technical constants like PATH_SEPARATOR.
Applied to files:
application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.javaapplication-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java
🧬 Code graph analysis (3)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
CacheMapKeys(3-7)
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
CacheMapKeys(3-7)
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/ActorTransformer.java (1)
ActorTransformer(10-131)
🪛 GitHub Actions: Pull Request
application-engine/src/main/java/com/netgrif/application/engine/importer/service/FunctionFactory.java
[error] 29-29: Compilation error: cannot find symbol 'GLOBAL' in Scope used by FunctionFactory.java.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
- GitHub Check: task-list-completed
🔇 Additional comments (51)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/version/Version.java (2)
71-78: LGTM!The
isHigherThanconvenience method correctly delegates tocompareToand improves code readability. The implementation and documentation are both accurate.
80-87: LGTM!The
isLowerThanconvenience method correctly delegates tocompareToand provides a readable alternative to direct comparisons. The implementation and documentation are both accurate.application-engine/src/test/groovy/com/netgrif/application/engine/TestHelper.groovy (1)
94-96: Clarify the need for bothclearFunctionCache()andclearGlobalFunctionCache()in test cleanup.These methods clear distinct caches:
clearGlobalFunctionCache()clears global-scoped functions (keyed by petriNetId), whileclearFunctionCache()clears individual cached functions. Verify both are necessary for proper test isolation, and if so, add a brief comment explaining their separate purposes.application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xml (1)
220-220: No action needed—change is correct and aligns with codebase migration.The method
getNewestVersionByIdentifierhas been removed project-wide. The change togetActiveVersionByIdentifieris the only available option and is already used throughout the test suite (ExportServiceTest.groovy, ImporterTest.groovy, and production code in WorkflowService, MenuImportExportService, FilterImportExportService). This change is necessary and correct.nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/FunctionScope.java (2)
3-7: LGTM: Clear enum-level documentation.The JavaDoc provides a concise description of the enum's purpose.
10-18: Deprecation handled: FunctionScope.NAMESPACE replaced with GLOBAL and automatically migrated.The NAMESPACE enum constant has been replaced with GLOBAL in the FunctionScope enum. However, this change is managed through automatic deprecation handling, not an uncontrolled breaking change:
- Automatic migration: FunctionFactory.checkDeprecatedAttributes() (lines 26–30) automatically converts any imported function with scope="namespace" to GLOBAL and logs a deprecation warning.
- Test data compatibility: Existing test XML files (function_res.xml, function_test.xml, etc.) still use scope="namespace" and are transparently converted during import.
- Code migration: No remaining FunctionScope.NAMESPACE references in the codebase—already removed.
Minor improvements:
- Add @deprecated annotation for explicit API visibility
- Document this change in CHANGELOG for v7.0.0
- Include migration timeline in deprecation warning
External consumers calling fromValue("namespace") directly (rather than importing XML) will receive IllegalArgumentException, which is expected behavior for removed enum values.
Likely an incorrect or invalid review comment.
application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovy (1)
133-133: LGTM! Test updated to use active version API.The test correctly uses the new
getActiveVersionByIdentifiermethod to verify imported PetriNets, consistent with the API changes in this PR.Also applies to: 207-207
application-engine/src/test/resources/change_caseref_value_action_test.xml (1)
32-32: LGTM! Test resource updated consistently.The embedded scripts correctly use the new
getActiveVersionByIdentifiermethod, maintaining consistency with the API changes.Also applies to: 46-46
application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy (1)
131-131: LGTM! Helper methods updated to use active version.Both
upsertNetandimportProcessOncecorrectly usegetActiveVersionByIdentifierto check for existing PetriNets before creating new ones, which aligns with the intended semantics of these operations.Also applies to: 252-252
application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy (1)
314-314: LGTM! Test updated to use active version API.The test helper correctly uses
getActiveVersionByIdentifierto retrieve the impersonation config PetriNet, consistent with the API changes. Note that this test is currently disabled.application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java (1)
427-427: LGTM! Filter creation updated to use active version.The method correctly retrieves the active version of the "filter" PetriNet during filter case creation, which is appropriate for startup initialization logic.
application-engine/src/main/java/com/netgrif/application/engine/startup/runner/ImpersonationRunner.java (1)
42-42: The usage ofgetActiveVersionByIdentifierfor impersonation configs is correct and consistent with the codebase pattern. The active version semantics are appropriate here—all similar system and configuration processes (dashboard management, filters, menu, preferences) consistently use this method to retrieve the currently deployed version. The test suite already expects this behavior, and there is no pattern of using "newest" versions for system configurations at runtime.application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java (1)
153-166: Document the semantic difference between "active" and "latest" versions.The API now provides both
getActiveVersionByIdentifier(replacinggetNewestVersionByIdentifier) andgetLatestVersionByIdentifier. These methods should be documented to clarify:
- What makes a version "active" vs "latest"
- When callers should use one vs the other
- Whether they can return different results, and if so, under what conditions
This distinction is important for correct usage throughout the codebase, particularly given the replacement of "newest" with "active."
application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/RunnerConfigurationProperties.java (1)
59-59: Breaking change: Configuration property structure refactored.Renaming this field is part of a broader configuration structure migration. The external configuration changed from the old
nae.field-runner.cache-size.*prefix structure (e.g.,nae.field-runner.cache-size.namespace-functions) to the newnetgrif.engine.runner.field-runner.*structure with renamed properties (e.g.,netgrif.engine.runner.field-runner.globalFunctionsCacheSize). Existing configurations using the old property names will no longer work.Likely an incorrect or invalid review comment.
application-engine/src/test/resources/petriNets/process_version_1_0_0.xml (1)
1-9: LGTM!The XML structure is well-formed and correctly defines a test PetriNet resource for version 1.0.0, aligning with the active-version testing approach introduced in this PR.
application-engine/src/test/resources/petriNets/process_version_5_0_0.xml (1)
1-9: LGTM!The XML structure is well-formed and correctly defines a test PetriNet resource for version 5.0.0.
application-engine/src/test/resources/petriNets/process_version_3_0_0.xml (1)
1-9: LGTM!The XML structure is well-formed and correctly defines a test PetriNet resource for version 3.0.0.
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java (1)
269-272: Good defensive coding with the null check.The addition of the null check for the active filter PetriNet prevents potential NPE in subsequent operations. The switch from newest to active version aligns with the PR's objectives.
application-engine/src/test/resources/petriNets/process_version_2_0_0.xml (1)
1-9: LGTM!The XML structure is well-formed and correctly defines a test PetriNet resource for version 2.0.0.
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java (1)
27-27: LGTM!The
versionActivefield is properly added, initialized in the constructor, and updated in theupdatemethod. This correctly mirrors the PetriNet's active-version state in the Elastic representation.Also applies to: 43-43, 52-52
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java (1)
288-288: Verify which version method was previously used for case creation.The review comment references a change from
getNewestVersionByIdentifiertogetActiveVersionByIdentifier, butgetNewestVersionByIdentifierdoes not exist in the codebase. The codebase containsgetLatestVersionByIdentifier(which retrieves the highest version number) andgetActiveVersionByIdentifier(which retrieves the version marked active). Clarify whether the change is fromgetLatestVersionByIdentifieror another method.The underlying concern about semantic differences between active and latest versions remains valid:
getActiveVersionByIdentifierreturns versions with an active flag, whilegetLatestVersionByIdentifierreturns versions sorted by version number. If the change is confirmed, verify that all callers (FilterImportExportService, MenuItemService, ActionApiImpl) are aware of this distinction and that test coverage includes scenarios where newest and active versions differ.Likely an incorrect or invalid review comment.
application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy (1)
139-139: LGTM: Improved error message clarity.The updated exception message is more specific and actionable, including the process identifier and version number. This aligns with better error reporting practices.
application-engine/src/test/resources/petriNets/process_version_4_0_0.xml (1)
1-9: LGTM: Test resource completes version coverage.This test resource follows the established pattern of other process version test files and fills the gap for version 4.0.0 in the test suite.
application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java (1)
68-72: Good defensive null check; consistent with DashboardItemServiceImpl.The defensive null check and error handling follow the same pattern as DashboardItemServiceImpl, which is good for consistency. The same verification regarding "active" vs "newest" version semantics applies here.
Ensure that the "active" version retrieval is appropriate for dashboard management case creation, consistent with the broader architectural decision reflected in DashboardItemServiceImpl and other services.
application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java (1)
36-43: LGTM: Well-documented repository method.The new repository method follows Spring Data MongoDB naming conventions correctly and will be automatically implemented. The Javadoc is clear and properly formatted.
application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy (2)
131-131: LGTM: Test updated for active version semantics.The test correctly uses
getActiveVersionByIdentifierinstead ofgetNewestVersionByIdentifier, aligning with the broader architectural changes in the codebase.
149-149: LGTM: Consistent version retrieval in test.This change is consistent with line 131 and the broader test updates across the repository.
application-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy (1)
48-48: LGTM: Test updated for active version semantics.This change aligns with the consistent pattern across all test files, replacing
getNewestVersionByIdentifierwithgetActiveVersionByIdentifier.application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/FieldActionsRunner.groovy (1)
80-86: Cache method rename verified.The method has been successfully renamed from
getNamespaceFunctionCache()togetGlobalFunctionsCache(). The new method is defined in both the interface and implementation, and the usage in FieldActionsRunner.groovy is correct. No references to the old method name remain in the codebase.application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java (1)
66-70: Good defensive null check; pattern aligns with codebase standards.The null check and clear exception message are good defensive programming. The use of
getActiveVersionByIdentifieris consistent with the established pattern throughout the codebase—including similar services likeDashboardManagementServiceImpl—for retrieving process versions when creating cases. No competing "newest version" method exists; the active version retrieval is the standard, intended approach used consistently across workflow operations.application-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java (1)
240-240: LGTM! Consistent migration to active version lookups.The switch from
getNewestVersionByIdentifiertogetActiveVersionByIdentifieraligns with the broader refactoring across the codebase. Both call sites have appropriate null checks and error messages.Also applies to: 264-264
application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy (1)
2447-2453: LGTM! Proper null guard and version lookup.The migration to
getActiveVersionByIdentifierwith a null check and descriptive exception message is consistent with similar changes inRoleActionDelegate.groovyand across the codebase.application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy (1)
54-57: LGTM! Consistent active version lookup with proper null guards.The migration to
getActiveVersionByIdentifierwith null checks and formatted error messages is consistent with the broader refactoring pattern across the codebase.Also applies to: 81-84
application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy (1)
70-77: LGTM! Test updated to reflect active version semantics.The test correctly uses
getPetriNetActive()andgetActiveVersionByIdentifier()to align with the refactored cache configuration and PetriNet service API.nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java (1)
76-79: LGTM! Well-structured active version flag implementation.The
versionActivefield with@Indexedannotation, proper initialization in constructors, and helper methods (makeActive(),makeInactive()) provide a solid foundation for the active/latest version distinction introduced in this PR.Also applies to: 166-166, 177-177, 441-447
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java (1)
24-24: LGTM! Method renaming aligns with global cache terminology.The renaming from
getNamespaceFunctionCache()togetGlobalFunctionsCache()andclearNamespaceFunctionCache()toclearGlobalFunctionCache()is consistent with the broader cache refactoring (e.g.,CacheMapKeys.FUNCTIONS,GenericMapCache) introduced in this PR. The newcacheAllPetriNetFunctions()method provides a clear entry point for bulk cache population.Also applies to: 30-33
application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy (2)
303-305: LGTM! Helper method standardizes PetriNet import calls.The
importProcesshelper method centralizes the import logic and improves test readability. It's consistently used across existing and new tests.
150-212: LGTM! Comprehensive tests for active version behavior.The new tests (
testVersionsOnImportandtestVersionActiveOnDelete) thoroughly validate:
- Version activation on import
- Version switching and state propagation to Elasticsearch
- Error handling for invalid version imports
- Version activation on delete (oldest version becomes active)
These tests provide solid coverage for the active version feature introduced in this PR.
Also applies to: 214-241
application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java (2)
29-52: LGTM! Cache naming changes align with the new active/latest versioning semantics.The renaming from
petriNetNewesttopetriNetActiveand the addition ofpetriNetLatestandglobalFunctionscache names are consistent with the PR's objective to support distinct active vs. latest version caching.
66-71: Cache set construction is correct.The
getAllCaches()method properly includes all configured cache names in aLinkedHashSetto maintain order and avoid duplicates.application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
34-64: Per-key locking for compute-if-absent is well-implemented.The double-checked locking pattern with per-key locks minimizes contention while ensuring thread-safe lazy loading. The
finallyblock correctly cleans up the lock entry.One minor note: the unchecked cast at lines 39, 47, and 57 could benefit from
@SuppressWarnings("unchecked")on the method to suppress compiler warnings.🔎 Optional: suppress unchecked warning
@Override + @SuppressWarnings("unchecked") public <T> T get(Object key, Callable<T> loader) {application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (2)
27-30: Good use of@RequiredArgsConstructorand migration toCachingConfigurer.The switch from
CachingConfigurerSupport(deprecated) to implementingCachingConfigurerdirectly is the correct modern approach.
42-58: Thread-safe bounded maps are correctly configured.Using
Collections.synchronizedMap(new MaxSizeHashMap<>(...))addresses the thread-safety concern raised in previous reviews. The cache sizes are properly sourced fromfieldRunnerProperties.application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (2)
114-134: Good batched processing with error handling incacheAllPetriNetFunctions.The pagination approach prevents loading all PetriNets into memory at once, and the try-catch ensures one failing net doesn't abort the entire operation. The warning log provides good observability.
193-199: Well-implementedgetRequiredCachehelper.This centralized null-check with a clear error message addresses previous review feedback and prevents NPEs throughout the service.
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (6)
8-11: Good: Explicit imports instead of wildcard.This addresses the previous review comment about avoiding
*imports for consistency and preventing type shadowing.
140-154: Cache eviction correctly updated for new active/latest semantics.Both
evictAllCaches()andevictCache()now properly clear/evict frompetriNetActiveandpetriNetLatestcaches, consistent with the renamed cache configuration.
181-252: Comprehensive import flow with proper version management.The
@Transactionalannotation ensures atomicity across the import operation. The version handling logic correctly:
- Prevents duplicate version imports
- Auto-increments version when not specified
- Rejects lower versions than existing latest
- Maintains single active version invariant by inactivating previous
One consideration: the
inactivatedProcessis saved beforenewProcess(lines 243-246). IfdoSaveInternal(newProcess)fails afterdoSaveInternal(inactivatedProcess)succeeds, the transaction rollback should revert both—verify that the MongoDB transaction configuration supports this.
254-271: Good refactoring:doSaveInternalconsolidates save logic.Extracting the common save operations (initialize arcs, evict cache, persist, index) into a protected method improves maintainability and ensures consistency.
310-331: New caching methods properly guard against null identifiers.Both
getActiveVersionByIdentifierandgetLatestVersionByIdentifierinclude null checks and use appropriate@Cacheableannotations withunless = "#result == null"to avoid caching null results.The
getLatestVersionByIdentifierimplementation correctly queries with descending sort on version components to find the highest version.
596-604: Correct fallback: Activates latest version when active version is deleted.This maintains the invariant that there should always be an active version (if any versions exist) by promoting the latest version when the currently active one is deleted.
| @Override | ||
| public synchronized <T> T get(Object key, Class<T> type) { | ||
| String stringKey = String.valueOf(key); | ||
| Object valueObject = map().get(stringKey); | ||
| return valueObject != null ? type.cast(valueObject) : null; | ||
| } | ||
|
|
||
| @Override | ||
| public synchronized void put(Object key, Object value) { | ||
| map().put(String.valueOf(key), safeCast(value)); | ||
| } | ||
|
|
||
| @Override | ||
| public synchronized void evict(Object key) { | ||
| map().remove(String.valueOf(key)); | ||
| } | ||
|
|
||
| @Override | ||
| public synchronized void clear() { | ||
| this.atomicMapRef.set(mapFactory.get()); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Mixed synchronization strategy creates potential inconsistencies.
The class uses three different synchronization mechanisms:
- Per-key locks in
get(key, loader)(lines 42-63) - Method-level
synchronizedonget(key, type),put,evict,clear(lines 74, 81, 86, 91) AtomicReferencefor map swapping inclear()
This mix can lead to visibility issues. For example, a thread calling put() acquires the instance monitor, while another thread in get(key, loader) only holds a per-key lock—they don't synchronize with each other.
Consider using a consistent approach: either use ConcurrentHashMap operations throughout (removing synchronized), or use the instance monitor for all mutations.
| /** | ||
| * The size of the cache used for maintaining field runner namespaces. | ||
| */ | ||
| private int namespaceCacheSize = 500; | ||
| private int globalFunctionsCacheSize = 500; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update Javadoc to match the renamed field.
The Javadoc on line 57 still references "maintaining field runner namespaces," but the field has been renamed to globalFunctionsCacheSize. Update the documentation to accurately describe the cache's purpose.
🔎 Apply this diff to fix the Javadoc:
/**
- * The size of the cache used for maintaining field runner namespaces.
+ * The size of the cache used for managing global field runner functions.
*/
private int globalFunctionsCacheSize = 500;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /** | |
| * The size of the cache used for maintaining field runner namespaces. | |
| */ | |
| private int namespaceCacheSize = 500; | |
| private int globalFunctionsCacheSize = 500; | |
| /** | |
| * The size of the cache used for managing global field runner functions. | |
| */ | |
| private int globalFunctionsCacheSize = 500; |
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/RunnerConfigurationProperties.java
around lines 56 to 59, the Javadoc still describes "maintaining field runner
namespaces" but the field was renamed to globalFunctionsCacheSize; update the
Javadoc to describe that this integer controls the size of the cache used for
global function definitions (or global functions lookup) so the comment
accurately reflects the field's purpose and name.
| private void checkDeprecatedAttributes(com.netgrif.application.engine.objects.importer.model.Function function) { | ||
| if (function.getScope() != null && function.getScope().value().equals(NAMESPACE)) { | ||
| log.warn("Function scope [NAMESPACE] is deprecated. Replacing with [GLOBAL].]"); | ||
| function.setScope(Scope.GLOBAL); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "Scope.java" | head -20Repository: netgrif/application-engine
Length of output: 52
🏁 Script executed:
#!/bin/bash
# Search for enum Scope definition
rg "enum\s+Scope" -A 10 --type javaRepository: netgrif/application-engine
Length of output: 52
🏁 Script executed:
#!/bin/bash
# Search for Scope class/interface definition
rg "^(class|interface|enum)\s+Scope\b" --type java -A 15Repository: netgrif/application-engine
Length of output: 52
🏁 Script executed:
#!/bin/bash
# Read the FunctionFactory.java file to see imports and the actual method
cat application-engine/src/main/java/com/netgrif/application/engine/importer/service/FunctionFactory.java | head -50Repository: netgrif/application-engine
Length of output: 1320
🏁 Script executed:
#!/bin/bash
# Search for Scope enum in the objects.importer.model package
find . -type f -path "*/objects/importer/model/*" -name "*.java" | xargs ls -laRepository: netgrif/application-engine
Length of output: 1212
🏁 Script executed:
#!/bin/bash
# Find all Java files in objects/importer/model path
find . -path "*/objects/importer/model/*.java" -type fRepository: netgrif/application-engine
Length of output: 52
🏁 Script executed:
#!/bin/bash
# Search for Scope.java file anywhere in the objects directory
find . -path "*/objects/*" -name "Scope.java" -type fRepository: netgrif/application-engine
Length of output: 52
🏁 Script executed:
#!/bin/bash
# Broad search for where Scope is defined
rg "class Scope|interface Scope" --type java -B 2 -A 10Repository: netgrif/application-engine
Length of output: 52
🏁 Script executed:
#!/bin/bash
# Search for GLOBAL constant in the codebase
rg "GLOBAL" --type java | head -20Repository: netgrif/application-engine
Length of output: 2792
🏁 Script executed:
#!/bin/bash
# Search for Scope in nae-object-library or find the importer model package
find . -path "*/importer/model/*" -name "*.java" -type fRepository: netgrif/application-engine
Length of output: 52
🏁 Script executed:
#!/bin/bash
# List all directories in nae-object-library
find ./nae-object-library -type d -name "importer"Repository: netgrif/application-engine
Length of output: 52
🏁 Script executed:
#!/bin/bash
# Search all Java files for "Scope.GLOBAL"
rg "Scope\." --type java -B 2 -A 2 | head -40Repository: netgrif/application-engine
Length of output: 3040
🏁 Script executed:
#!/bin/bash
# Search for all imports of Scope in FunctionFactory and related files
rg "import.*Scope" --type java | grep -v "FunctionScope"Repository: netgrif/application-engine
Length of output: 742
🏁 Script executed:
#!/bin/bash
# Find the Scope class in the objects.importer.model package
find . -path "*/objects/importer/model/Scope.java" -o -path "*/objects/importer/model/Scope.class"Repository: netgrif/application-engine
Length of output: 52
🏁 Script executed:
#!/bin/bash
# List all files in the objects/importer/model directory
find . -type d -path "*/objects/importer/model" -exec ls -la {} \;Repository: netgrif/application-engine
Length of output: 52
🏁 Script executed:
#!/bin/bash
# Search for where the importer model package is defined
find . -type d -name "importer"Repository: netgrif/application-engine
Length of output: 351
Critical: Fix compilation error - Scope.GLOBAL does not exist.
The code on line 29 attempts to use Scope.GLOBAL, but the Scope class from com.netgrif.application.engine.objects.importer.model cannot be found in the codebase. This will result in a compilation error: "cannot find symbol 'GLOBAL' in Scope" or more likely "cannot find symbol 'Scope'".
Verify the correct import path for the Scope enum and ensure the constant exists, or refactor to use an alternative approach (such as directly setting an appropriate value on the importer model function).
🧰 Tools
🪛 GitHub Actions: Pull Request
[error] 29-29: Compilation error: cannot find symbol 'GLOBAL' in Scope used by FunctionFactory.java.
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/importer/service/FunctionFactory.java
around lines 26 to 31, the code references Scope.GLOBAL from
com.netgrif.application.engine.objects.importer.model but that Scope
type/constant isn't available, causing a compilation error; to fix, find the
correct Scope enum/class used by the importer model (search the project for the
Scope definition or an equivalent enum like FunctionScope), import that type at
the top of the file, and replace the current reference with the correct enum
constant or conversion method (e.g., Scope.GLOBAL or Scope.valueOf("GLOBAL") or
Scope.fromValue("GLOBAL")) or, if no enum exists, set the appropriate
string/value on the importer Function (e.g., function.setScope("GLOBAL") or use
the model's setter/creator API); ensure the import statement is added and the
referenced constant/method matches the actual enum/class name to resolve the
compilation error.
| 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()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Potential NullPointerException from null PetriNet values.
The getActiveVersionByIdentifier call within the stream can return null if a process identifier has no active version. These null values will be collected into the petriNets list, and the subsequent isEmpty() check at line 285 won't catch them. When the code iterates over petriNets at line 302, calling net.getImmediateFields() will throw a NullPointerException.
This could be the source of the test errors reported by Retoocs.
🔎 Apply this diff to filter out null values:
} else {
- petriNets = processes.stream().map(process -> petriNetService.getActiveVersionByIdentifier(process)).collect(Collectors.toList());
+ petriNets = processes.stream()
+ .map(process -> petriNetService.getActiveVersionByIdentifier(process))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| petriNets = processes.stream().map(process -> petriNetService.getActiveVersionByIdentifier(process)).collect(Collectors.toList()); | |
| petriNets = processes.stream() | |
| .map(process -> petriNetService.getActiveVersionByIdentifier(process)) | |
| .filter(Objects::nonNull) | |
| .collect(Collectors.toList()); |
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java
around line 283, the stream maps processes to petriNets using
petriNetService.getActiveVersionByIdentifier which can return null, causing
NullPointerExceptions later when iterating and calling net.getImmediateFields();
filter out null results after the map (e.g., add a filter(Objects::nonNull) or
equivalent) before collecting into petriNets, or alternatively replace nulls
with a safe placeholder and handle them explicitly; ensure subsequent checks
consider an empty list after filtering so iteration over petriNets never
encounters null entries.
| @Override | ||
| public Map<String, List<CachedFunction>> getNamespaceFunctionCache() { | ||
| return new HashMap<>(namespaceFunctionsCache); | ||
| public Map<String, List<CachedFunction>> getGlobalFunctionsCache() { | ||
| return new HashMap<>((Map<String, List<CachedFunction>>) getRequiredCache(properties.getGlobalFunctions()).getNativeCache()); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unchecked cast in getGlobalFunctionsCache may throw ClassCastException.
The cast (Map<String, List<CachedFunction>>) assumes getNativeCache() returns a Map, but if the cache implementation changes, this will fail at runtime. Add a defensive check:
🔎 Suggested fix:
@Override
public Map<String, List<CachedFunction>> getGlobalFunctionsCache() {
- return new HashMap<>((Map<String, List<CachedFunction>>) getRequiredCache(properties.getGlobalFunctions()).getNativeCache());
+ Object nativeCache = getRequiredCache(properties.getGlobalFunctions()).getNativeCache();
+ if (nativeCache instanceof Map<?, ?> map) {
+ @SuppressWarnings("unchecked")
+ Map<String, List<CachedFunction>> typedMap = (Map<String, List<CachedFunction>>) map;
+ return new HashMap<>(typedMap);
+ }
+ return new HashMap<>();
}🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
around lines 173 to 176, the unchecked cast from
getRequiredCache(...).getNativeCache() to Map<String, List<CachedFunction>> can
throw ClassCastException if the native cache is not a Map; instead, check
whether getNativeCache() is an instance of Map before casting, and only perform
a safe, typed copy when it is (casting the raw Map entries to the expected
generic types defensively), otherwise return an empty HashMap or convert the
cache to the expected Map form; ensure no unchecked cast remains and handle the
non-Map case gracefully (log or fallback) so runtime failures are avoided.
| PetriNet filterNet = petriNetService.getNewestVersionByIdentifier(FILTER_NET_IDENTIFIER); | ||
| PetriNet filterNet = petriNetService.getActiveVersionByIdentifier(FILTER_NET_IDENTIFIER); | ||
| if (filterNet == null) { | ||
| throw new IllegalStateException("No filter process found or active"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider clarifying the error message.
The message "No filter process found or active" could be more explicit about whether the process doesn't exist or simply isn't active.
🔎 Apply this diff to clarify:
- throw new IllegalStateException("No filter process found or active");
+ throw new IllegalStateException("No active filter process with identifier '" + FILTER_NET_IDENTIFIER + "' found");📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| throw new IllegalStateException("No filter process found or active"); | |
| throw new IllegalStateException("No active filter process with identifier '" + FILTER_NET_IDENTIFIER + "' found"); |
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java
around line 271, improve the thrown exception message so it clearly
distinguishes between a missing filter process and an inactive one; change the
message to state "Filter process not found or not active" more explicitly and,
if available in scope, append identifying details (e.g., filter/process id or
name) and the state (missing vs. inactive) so the exception reads like "Filter
process '[id/name]' not found" or "Filter process '[id/name]' found but not
active" depending on the condition.
| if (net == null) { | ||
| throw new IllegalArgumentException("Petri net with identifier [" + identifier + "] does not exist."); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider clarifying the error message.
The error message states "does not exist" but with the change to active-version lookup, the issue could be that the PetriNet exists but is not active. Consider making the error message more specific.
🔎 Apply this diff to clarify the error message:
- throw new IllegalArgumentException("Petri net with identifier [" + identifier + "] does not exist.");
+ throw new IllegalArgumentException("Active Petri net with identifier [" + identifier + "] not found.");Also applies to: 303-305, 312-314
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java
around lines 289-291 (also update similar checks at 303-305 and 312-314), the
IllegalArgumentException message "Petri net with identifier [...] does not
exist." is ambiguous after switching to active-version lookup; change the
message to distinguish between "does not exist" and "exists but no active
version found." Update each thrown exception to indicate that either the Petri
net was not found or that an active version is not available (e.g., "Petri net
with identifier [X] was found but no active version exists" or "Petri net with
identifier [X] does not exist or has no active version") so the error clearly
reflects the active-version lookup outcome.
| /** | ||
| * 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</0> | ||
| */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix malformed HTML tag in JavaDoc.
Line 59 contains </0> which is invalid HTML. Remove the closing tag.
🔎 Apply this diff to fix the JavaDoc:
/**
* 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</0>
+ * @return 0 if the versions equal, <0 if this is lower than other, >0 if this is higher than other
*/📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /** | |
| * 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</0> | |
| */ | |
| /** | |
| * 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 | |
| */ |
🤖 Prompt for AI Agents
In
nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/version/Version.java
around lines 55 to 60, the JavaDoc return description contains a malformed HTML
closing tag `</0>` on line 59; remove that invalid tag and replace it with the
intended plain text punctuation (e.g., a period) so the @return line reads
correctly: "0 if the versions equal, <0 if this is lower than other, >0 if this
is higher than other."
Description
This PR introduces a GenericMapCache implementation of Spring’s Cache interface, backed by in-memory Maps. The solution replaces ad-hoc local maps with proper cache wrappers that integrate with Spring Boot Actuator’s /caches endpoints.
This makes all cache operations consistent and manageable via Actuator.
Implements NAE-2182
Blocking PRs
This PR depends on NAE-2212
How Has Been This Tested?
The functionality was tested using Postman to verify cache operations through the Actuator endpoints.
Checklist:
Summary by CodeRabbit
New Features
Refactor
Bug Fixes
Tests
✏️ Tip: You can customize this high-level summary in your review settings.