System.getProperty() inside Workflow
- Engine
- Temporal Java SDK
- Since
- 0.3.0
- Auto Fix
- Not Available
- Category Range
- WG001–WG099
Description
A Temporal workflow implementation — or any code reachable from a workflow's entry
point, however many method calls away — calls System.getProperty(...).
Why This Matters
System properties are properties of the worker's JVM, not of the workflow. Temporal
workflows are replayed, sometimes on a different worker host or in a redeployed
environment: whenever a worker needs to reconstruct a workflow's state, it re-executes
the workflow's code from the beginning of event history. If workflow code reads a system
property directly, replay re-executes that read and may see a different value than the
original execution did (a different JVM configuration, a different host, a different
deployment) — and if any decision in the workflow depends on that value, replay diverges
from the original execution and raises a NonDeterministicException.
Violation Example
public class PaymentWorkflowImpl implements PaymentWorkflow {
@Override
public void processPayment() {
String home = System.getProperty("user.home");
}
}This is flagged even when the call is several methods away from the workflow entry point:
public class PaymentWorkflowImpl implements PaymentWorkflow {
private final ConfigService configService = new ConfigService();
@Override
public void processPayment() {
// WG009 still fires: WoGu's call-graph analysis follows this call into
// ConfigService.userHome() and finds System.getProperty() inside it.
configService.userHome();
}
}
class ConfigService {
String userHome() {
return System.getProperty("user.home");
}
}Compliant Example
public class PaymentWorkflowImpl implements PaymentWorkflow {
private final ConfigActivity configActivity;
@Override
public void processPayment() {
String home = configActivity.readUserHome();
}
}
// Implemented outside the workflow; not subject to replay determinism.
class ConfigActivityImpl implements ConfigActivity {
@Override
public String readUserHome() {
return System.getProperty("user.home");
}
}Recommended Fix
Read configuration outside the workflow or via an Activity. A value read once outside the workflow (e.g. passed in as a workflow argument) or read inside an Activity (which is not replayed) is stable across replay by construction.
False Positives
WG009 uses the same call-graph analysis as WG001–WG008: starting
from a workflow's entry-point method(s), it follows every method call that can be
resolved to another method's source within the same project, however many hops deep,
looking for System.getProperty(...) along the way. Matching is by method name only, so
both the single-argument and two-argument (with a default value) overloads are flagged
identically.
As with the other determinism rules, this analysis intentionally stops, without
reporting anything past that point, at two kinds of boundary: a call it cannot resolve to
source it can see, and a call that resolves into a Temporal Activity implementation
(recognized by its @ActivityInterface/@ActivityMethod annotations, or by resolving
only as far as the activity interface's bodyless method) — activity code is not replayed,
so it is exempt from this rule by design.
This means WG009 can produce false negatives (a real violation hidden behind a call
it can't see into) but is designed to avoid false positives — it must resolve to
System.getProperty(...) specifically. If you believe WG009 is flagging or missing
something incorrectly, please open an issue with a minimal reproduction.
Found an issue with this rule's detection? Open an issue with a minimal reproduction.