The problem:
The problem appears in a k8s pod running spring-boot, java 8 (more details below)
When using ObjectProvider<>
and calling *provider.getObject(.....)*
, on
a prototype bean defined in Spring Configuration, every now and then (never
find a way to make it happen regularly) setter injection methods are not called.
Update oct, 2: See Spring Issue #25840
Most of the time this works perfectly well, but sometimes, it constructs a new object but misses to call the @Autowired setter method (checked with log information).
We also discover that 90% of the time it happens after application startup, but not always.
UPDATE sept, 21: Deleting (Restarting) de pod solves the problem.
UPDATE sept, 22: Once it happens it will happen all the time, I mean is not that it fails once, and the next time it works ok, it will always fail to call setters.
UPDATE sept, 23: New evidence related to concurrency problem in the real application, didn't appear until now, a single thread alone seemed to generate the problem. (take a look at classes below to understand better this description)
ToipParentClass (this is a Strategy Implementation) has setter @Autowired for
- VlocityService
- OrderManagemenService
InternetParentClass (this is a Strategy Implementation) has setter @Autowired for
- VlocityService
- OrderManagemenService
Log (commented)
[-nio-80-exec-10] GuidTaskController : Build Strategy using XOM_TOIP_OFFER .
[p-nio-80-exec-2] GuidTaskController : Build Strategy using XOM_INTERNET_OFFER .
[-nio-80-exec-10] ToipParentClass : @Constructing ToipParentClass
[p-nio-80-exec-2] InternetParentClass : @Constructing InternetParentClass
[-nio-80-exec-10] ToipParentClass : @Autowiring VlocityServices@7951cd46
[p-nio-80-exec-2] InternetParentClass : @Autowiring VlocityServices@7951cd46
[-nio-80-exec-10] ToipParentClass : @Autowiring OrderManagementService@3385326a
-------------------------------------------------------------
ERROR !!! Missing @Autowiring log
[p-nio-80-exec-2] InternetParentClass : @Autowiring OrderManagementService@7951cd46
-------------------------------------------------------------
[p-nio-80-exec-2] Controller : Unexpected Error
2020-09-22T18:56:45.544525916Z
-------------------------------------------------------------
ERROR: NullPointer when using not set OrderManagementService
-------------------------------------------------------------
2020-09-22T18:56:45.544530395Z java.lang.NullPointerException: null
2020-09-22T18:56:45.544534074Z at InternetParentClass.generateIds(InternetParentClass.java:50) ~[classes!/:BUG001_PrototypeBeanAutowired-8]
2020-09-22T18:56:45.544538568Z at GuidTaskController.setGUID(GuidTaskController.java:180) ~[classes!/:BUG001_PrototypeBeanAutowired-8]
I made a simple test in https://github.com/toniocus/strategy-calculator run it standalone, different jdk8 versions, also in the same docker image used in the pod (all things in the project), and failed to make it FAIL.
Any ideas on where to look for a problem, suggestions on what to try, or even a solution :-), will be greatly welcome, thanks in advance
Below the product versions, classes.
Detail about versions:
k8s:
v1.14.9-eks-658790
spring-boot:
2.1.4.RELEASE
JDK:
openjdk version "1.8.0_212"
OpenJDK Runtime Environment (IcedTea 3.12.0) (Alpine 8.212.04-r0)
OpenJDK 64-Bit Server VM (build 25.212-b04, mixed mode)
The Classes
A snippet on the classes involved, no real code, do not worry about syntax errors. (real code can be found in https://github.com/toniocus/strategy-calculator)
// ============================================================
public interface Strategy {
void work(...);
}
// ============================================================
@Service
public class Service {
public void doSomething() {
......
......
}
}
// ============================================================
public class MobileStrategy implements Strategy {
private Service service;
@Autowired
public void setService(Service s) {
this.service = s; // setter not called every now and then
}
public void work(...) {
this.service.doSomething(); // throws NullPointerException every now an then
}
}
// ============================================================
public enum StrategyEnum {
MOBILE("mobileKey", MobileStrategy.class),
TV("tvKey", TvStrategy.class),
.......
public Class<Strategy> getImplementationClass() {
......
}
public StrategyEnum findByKey(String key) {
.....
}
}
// ============================================================
@Configuration
public class Configuration {
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Strategy getStrategy(final StrategyEnum selectorEnum) {
try {
Constructor<? extends Strategy> constructor =
selectorEnum.getImplementationClass().getConstructor();
return constructor.newInstance();
}
catch (Exception ex) {
throw new Exception("Not able to instantiate interface implementation"
+ " for enum: " + selectorEnum
, ex);
}
}
}
// ============================================================
@RestController
public class MathOperRestController {
@Autowired
ObjectProvider<Strategy> provider;
@GetMapping("/{operation}/{x}/{y}")
public BigDecimal add(
@PathVariable("operation") final String operation
, @PathVariable("x") final BigDecimal x
, @PathVariable("y") final BigDecimal y
) {
Strategy strategy = this.provider.getObject(StrategyEnum.findByKey(operation));
strategy.doWork(x, y);
return BigDecimal.ZERO;
}
UPDATE sept,21 added Service class to samples