1

I am writing a simple JSF application for converting currencies.

There is a Rates class which holds the data, Currencies class for managing requests and Manage class for adding new currencies. My problem is: I want the currencies to be persisted as a property of Rates class hence my use of @ApplicationScoped. However I can see that Rates keeps getting reinitialized with every single request. Here's my code of Rates:

@ManagedBean
@ApplicationScoped
public class Rates {

private Map<String, Double> rates = new HashMap<String, Double>();
private Set<String> currencies = new HashSet<String>(){
    {
        System.out.println("HashSet initializing");
    }
};
private List<String> list = new ArrayList<String>(Arrays.asList("Filip", "Kasia"));

public List<String> getList() {
    return list;
}

public void setList(List<String> list) {
    this.list = list;
}

public Rates() {
    rates.put("PLN_EUR", 0.25);
    rates.put("EUR_PLN", 4.0);
    currencies.add("PLN");
    currencies.add("EUR");
}

public Map<String, Double> getRates() {
    return rates;
}

public void setRates(Map<String, Double> rates) {
    this.rates = rates;
}

public Set<String> getCurrencies() {
    return currencies;
}

public void setCurrencies(Set<String> currencies) {
    this.currencies = currencies;
}

public void addRate(String firstCurrency, String secondCurrency){
    if(rates.containsKey(firstCurrency + "_" + secondCurrency))
        return;
    double rate = (double)(Math.random()*10);
    rates.put(firstCurrency + "_" + secondCurrency, rate);
    rates.put(secondCurrency + "_" + firstCurrency, 1 / rate);
    currencies.add(firstCurrency);
    currencies.add(secondCurrency);
    System.out.println(currencies);

}

public double getRate(String currencies){
    return rates.get(currencies);
}
}

and here's how it get's injected:

@ManagedBean
public class Manage {
private String firstCurrency;
private String secondCurrency;
private @Inject Rates rates;

public String getSecondCurrency() {
    return secondCurrency;
}

public void setSecondCurrency(String secondCurrency) {
    this.secondCurrency = secondCurrency;
}

public String getFirstCurrency() {
    return firstCurrency;
}

public void setFirstCurrency(String firstCurrency) {
    this.firstCurrency = firstCurrency;
}

public void addCurrencies(){
    rates.addRate(firstCurrency, secondCurrency);
}


}

As you can see I added instance initializer for a property of Rates, set currencies and the text "HashSet initializing" is printed out with every request.

BalusC
  • 992,635
  • 352
  • 3,478
  • 3,452
Broccoli
  • 1,059
  • 12
  • 19

1 Answers1

3

@Inject only injects CDI managed beans.

@ManagedBean declares a JSF managed bean not a CDI managed bean.

Make the Rates class a CDI managed bean instead.

import javax.inject.Named;
import javax.enterprise.context.ApplicationScoped;

@Named
@ApplicationScoped
public class Rates {}

Noted should be that the general recommendation is to stop using JSF managed bean facility and exclusively use CDI managed bean facility. So it'd be better if you make all your other JSF managed beans CDI managed beans too. That's exactly why I wouldn't recommend to replace @Inject by its JSF equivalent @ManagedProperty (which would also have worked).

As to managed bean initialization, you should not do it at class/instance level, but perform the task in a @PostConstruct annotated method.

private Map<String, Double> rates;
private Set<String> currencies;
private List<String> list;

@PostConstruct
public void init() {
    rates = new HashMap<>();
    currencies = new HashSet<>();
    list = new ArrayList<>(Arrays.asList("Filip", "Kasia"));
}

This is particularly important in CDI because it creates and injects proxies based on the class, so the class may be instantiated at times you don't expect.

See also:

Community
  • 1
  • 1
BalusC
  • 992,635
  • 352
  • 3,478
  • 3,452
  • Hmm, i feel pretty dazed from hours of programming but I think I applied all the changes you suggested which are: 1. Adding `@Named` annotation to all my 3 classes 2. Moving the initialization to `@PostConstruct` annotated method but the issue is still unresolved. Is there anything I skipped, or do you have any more ideas of what may cause the problem? – Broccoli Apr 10 '16 at 15:17
  • Did you also remove the `@ManagedBean` (any annotations from `javax.faces.bean` package basically)? Did you also move the `System.out.println()` from instance initialization into `@PostConstruct`? – BalusC Apr 10 '16 at 15:20
  • Ah, ok, i left `ApplicationScoped` imported from `javax.faces.bean` not from `javax.enterprise.context`. Now when I changed it I ran into some ServletFaces exception, I will let you know when everything works properly. That's for help! – Broccoli Apr 10 '16 at 15:41