简体   繁体   中英

Scope of Singletons

I'm using a singleton bean to provide configuration values stored in a database to my Java EE application.

@Singleton
public class ConfigurationProvider {

  private final Map<String, ConfigurationEntity> configMap = new HashMap<>();

  @PersistenceContext(unitName = DatabaseConstants.PERSISTENCE_UNIT)
  private EntityManager em;

  public String getConfiguration(String key) {
    if (configMap.containsKey(key)) {
        return configMap.get(key).getValue();
    }

    ConfigurationEntity config = em.find(ConfigurationEntity.class, key);
    em.detach(config);
    if (config == null) {
        throw new RuntimeException("Configuration not found for " + key);
    }
    configMap.put(key, config);
    return config.getValue();
  }

  public void clear() {
    configMap.clear();
  }

  public Collection<ConfigurationEntity> getCurrentConfigurationState() {
    return configMap.values();
  }
 }

A Producer let me inject the values

public class ConfigurationProducer {

  @Inject
  private ConfigurationProvider configProvider;

  @Produces
  @ConfigurationValue
  public String getConfiguration(InjectionPoint ip) {
    String key = createKey(ip);
    return configProvider.getConfiguration(key);
  }

Here an example

@Inject
@ConfigurationValue
private Instance<String> endpoint;

This loads the endpont from the database. For testing reasons, the value should be changeable. So what you saw is part of an ejb module.

To reload the values, I created a REST-Interface that provides the functionality. This REST-Service is part of an additional WAR packaged together with the ejb module in one ear file.

@Path("/configuration")
public class ConfigurationResource {

  @EJB
  private ConfigurationProvider configurationProvider;

  @GET
  @Path("/current")
  @Produces({ "application/json" })
  public Collection<ConfigurationEntity> getCurrentConfiguration() {
    return configurationProvider.getCurrentConfigurationState();
  }
}

But the problem is, that the war has it's own instance of the configuration provider. So I cannot reaload the 'cache'. Why I have two instance of my singleton in the same ear?

I don't think you can use the ConfigurationProvider EJB that way. That EJB would need to have a remote interface, and you would access it as any remote EJB from the external WAR. The external WAR has a different class loader, hence it will not find EAR singleton EJBs.

It seems that you are using both CDI's @Inject and EJB's @EJB to inject your ConfigurationProvider instance. Considering that you are not synchronizing access to map and that you are using EntityManager, which is not thread safe, you propably should be using @EJB.

That said, you need just a minor change in your code:

public class ConfigurationProducer {
    @EJB
    private ConfigurationProvider configProvider;

    @Produces
    @ConfigurationValue
    public String getConfiguration(InjectionPoint ip) {
        String key = createKey(ip);
        return configProvider.getConfiguration(key);
}

Solution:

Maven packaged the ejb module a second time in the war's lib folder. I had to set the scope in the pom.xml to provided. With ejb modules it works without any exclutions but for the war you have to do it manually.

Now it works.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM