I am learning java EE by buildig a supply manager app. I'm trying to implement the strategy pattern, by having a SalesCalculatorService interface:
public interface SalesCalculatorService extends Serializable {
List<Number> getSalesData();
}
concrete Implementations like this one..
@Stateless
@MonthlySalesCalculatorQualifier
public class MonthlySalesCalculatorService implements SalesCalculatorService, Serializable{
private static final long serialVersionUID = 1L;
@Inject
private ProfitInvoiceService profitInvoiceService;
@Transactional
public List<Number> getSalesData(){
List<Number> salesList = new ArrayList<>();
Map<Month, List<ProfitInvoice>> salesMap = profitInvoiceService.getInvoicesPerMonth();
salesMap.keySet().forEach(e -> {
List<ProfitInvoice> invoiceList = salesMap.get(e);
double totalValue = invoiceList
.stream()
.mapToDouble(invoice -> {
ProfitInvoice profitInvoice = profitInvoiceService.findById(ProfitInvoice.class, invoice.getId());
return profitInvoice.getTotalAmount();
})
.sum();
salesList.add(Double.valueOf(totalValue));
});
return salesList;
}
}
A class with a method that produces a lambda that takes a string as an argument and returns the correct concrete implementation
public class SalesCalculatorStrategyProducer implements Serializable {
private static final long serialVersionUID = 1L;
@Produces @Named("salesCalculatorStrategy")
Function<String, SalesCalculatorService> salesCalculatorStrategyProducer = (String strategy) -> {
switch (strategy) {
case "monthly":
return new MonthlySalesCalculatorService();
case "quarter":
return new QuarterSalesCalculatorService();
default:
throw new IllegalArgumentException("Invalid strategy: " + strategy);
}
};
}
And a viewScoped bean where i would like to call the lambda to select implementation at runtime to display data in a chart
@Named
@ViewScoped
public class BarChartProductSales extends BarChartView {
private static final long serialVersionUID = 1L;
@Inject @Named("salesCalculatorStrategy")
private Function<String, SalesCalculatorService> salesCalculatorStrategyProducer;
@Override
public List<Number> getChartData() {
return salesCalculatorStrategyProducer.apply("quarter").getSalesData();
}
Unfortunately i get a serialization error when i try to load the page... All classes implement serializable and if i try to directly inject my concrete impletantion in the bean everything works fine. The error:
SEVERE: Servlet.service() for servlet [FacesServlet] in context with path [/manager] threw exception [jakarta.el.ELException: Error reading [barModel] on type [com.charts.BarChartProductSales$$OwbNormalScopeProxy0]] with root cause
jakarta.enterprise.inject.IllegalProductException: A producer method or field of scope @Dependent returns an unserializable object for injection into an injection point Field Injection Point, field name : salesCalculatorStrategyProducer, Bean Owner : [BarChartProductSales, WebBeansType:MANAGED, Name:barChartProductSales, API Types:[java.lang.Object,java.io.Serializable,com.charts.BarChartProductSales,com.charts.BarChartView], Qualifiers:[jakarta.enterprise.inject.Default,jakarta.enterprise.inject.Any,jakarta.inject.Named]] that requires a passivation capable dependency
at org.apache.webbeans.inject.AbstractInjectable.inject(AbstractInjectable.java:120)
at org.apache.webbeans.inject.InjectableField.doInjection(InjectableField.java:65)
at org.apache.webbeans.portable.InjectionTargetImpl.injectFields(InjectionTargetImpl.java:231)
at org.apache.webbeans.portable.InjectionTargetImpl.inject(InjectionTargetImpl.java:217)
at org.apache.webbeans.portable.InjectionTargetImpl.inject(InjectionTargetImpl.java:207)
at org.apache.webbeans.component.AbstractOwbBean.create(AbstractOwbBean.java:128)
at org.apache.webbeans.component.ManagedBean.create(ManagedBean.java:66)
Can Function type be serialized? I get the same error even if i simplify the method by using Function<String, String> so the classes shouldn't be the problem but rather the Function type itself?....
If i try to use the produces annotation to produce a simple string instead of a lambda the injection correctly works.. Any other way to do change implementation at runtime with the strategy pattern?