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.
Related
I want to build a JEE plugin based architecture. The main idea is do something similar to what eclipse is, but in the context of JEE.
My goal is to have a minimum of modules as the core, and allow others modules extend its functionality.
To this, I have implemented a test using 4 modules:
gauges: Defines and implements a gaugesregistry service, also defines a gauge POJO.
cashgauges: implements a gauge producer using CDI. this is a plugin mock.
othergauges: implements a gauge producer using CDI. this is a second plugin mock.
gauges-web: Contains a basic JSF view to query the gauges registry.
dependencies are as follows:
cashgauges --> gauges
othergauges --> gauges
gauges-web --> gauges
This is done by using jboss-deployment-structure.xml on each deployed file.
The deployment is done as individual files:
gauges.jar
cashgauges.jar
othergauges.jar
gauges-web.war
All services start, but what I see is, my gaugesregistry is instantiated several times. I started wildfly in debug mode and what I see is each module has its own instance of gaugesregistry: cashgauges and othergauges call same method (addGauge) on registry, but instances of this registry are not the same.
This happens in both cases, using #ApplicationScoped and #Singleton annotations. What am I doing wrong?
Source code is available on https://github.com/hatit/research
After a couple of days, I'm considering using a ServiceLocator pattern and remote references instead of CDI. Any suggestions?
Great, i got twice -2 votes (-4 reputation) because i asked an advanced topic for software developers?
I searched in about stackoverflow and found this
Founded in 2008, Stack Overflow is the largest, most trusted online community for developers to learn, share their knowledge, and build their careers...
If any interested in this topic, then:
After some hours understanding differences between CDI Beans and EJBs lifecycle when used as independent modules (JBoss Modules), i found:
Singleton CDI Beans are instantiated one time per module, not really singleton among all modules.
To avoid this i had to create Registry as a Singleton Enterprise Session Bean.
This cames with new problems, CDI injection doesn't works among modules, so i had to package a CDI producer (i don't care if it's singleton or not, its only a producer) which can be instantiated by any module. Main responsibility of this producer is to lookup Registry EJB, this to avoid hardcoding jndi path each time i need access the Registry.
I changed my trivial example to support JSF plugins also, this is an example of what i am using currently.
Module facelets:
Registry interface:
public interface FaceletsModuleRegistry {
void registerModule(String module);
List<String> getRegisteredModules();
}
Registry implementation:
#Local(FaceletsModuleRegistry.class)
#Singleton(name="FaceletsModuleRegistry")
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
#Vetoed
public class FaceletsModuleRegistryImpl implements FaceletsModuleRegistry {
private Set<String> registeredModuleNames = new TreeSet<>();
#Override
public void registerModule(String module) {
registeredModuleNames.add(module);
}
#Override
public List<String> getRegisteredModules() {
return Collections.unmodifiableList(new ArrayList<>(registeredModuleNames));
}
}
Registry producer:
#ApplicationScoped
public class FaceletsModuleRegistryBuilder {
#EJB(lookup="java:global/facelets/FaceletsModuleRegistry!co.hatit.enterprise.facelets.services.FaceletsModuleRegistry")
protected FaceletsModuleRegistry faceletsModuleRegistry;
#Produces
public FaceletsModuleRegistry getFaceletsModuleRegistry(){
return faceletsModuleRegistry;
}
}
Any other module that i want to plugin implements this code (please see #Inject can be used on any module requiring access the Registry singleton instance):
#ApplicationScoped
public class InmueblesActivator {
#Inject
private FaceletsModuleRegistry faceletsModuleRegistry;
public void init(#Observes #Initialized(ApplicationScoped.class) Object init){
String moduleName = Module.getCallerModule().getIdentifier().getName();
String name = StringUtils.substringBetween(moduleName, "deployment.", ".jar");
faceletsModuleRegistry.registerModule(name);
}
}
Then i can reference Registry from any module as a really singleton instance (solved my problem having multiple instances of same class when used CDI singleton beans among several modules).
Now, i can plugin JEE modules, not just java code, but facelets resources also:
public class FaceletsResourceHandler extends ResourceHandlerWrapper {
Logger logger = LoggerFactory.getLogger(FaceletsResourceHandler.class);
#Inject
FaceletsModuleRegistry faceletsModuleRegistry;
private ResourceHandler wrapped;
public FaceletsResourceHandler(ResourceHandler wrapped) {
this.wrapped = wrapped;
}
#Override
public ViewResource createViewResource(FacesContext context, final String name) {
ViewResource resource = super.createViewResource(context, name);
if (resource == null) {
resource = new ViewResource() {
#Override
public URL getURL() {
try {
//iterates over plugins to find the required resource.
for(String module : faceletsModuleRegistry.getRegisteredModules()){
URL resource = Module.getCallerModule().getModuleLoader()
.loadModule(ModuleIdentifier.create("deployment." + module + ".jar"))
.getExportedResource("META-INF/resources" + name);
if (resource != null) return resource;
}
} catch (ModuleLoadException e) {
throw new FacesException(e);
}
return null;
}
};
}
return resource;
}
#Override
public ResourceHandler getWrapped() {
return wrapped;
}
}
I have some beans (of multiple types, CDI, #Stateless and #Singleton beans). Some of their fields shall get injected from database values.
public class MyBean {
#Inject
#DbConfigValue(MyConfig.HOST)
String host;
}
So I added a custom #Qualifier (DbConfigValue) used by a Producer. The producer reads and caches config values from a database and injects them into the beans.
#Singleton
#Lock(LockType.READ)
public class Configuration {
#Produces
#Dependent
#DbConfigValue
public String getDbConfigValue(InjectionPoint point) {
// get key for the config-value from qualifier-annotation of the injected field
String key = point.getAnnotated().getAnnotation(DbConfigValue.class).value();
// i have read+cached database config values in #PostConstruct before
return cachedConfigValues.get(key);
}
}
This works well for initial injection / bean construction. Some web tutorials out there are suggesting this approach.
Now, I think it is reasonable to assume that config values, if stored in DB, might change at runtime. So, whenever an admin changes a database config value, I currently do fire a CDI-event.
Question: is there any way to re-inject values into fields of already-initialized bean-instances? Or is injection always related to instance-creation only?
E.g. I had s.th. similar to this in mind:
public class MyEventListener {
#Inject
BeanManager beanManager;
#Asynchronous
public void onDbConfigValueChangedEvent (#Observes(during = TransactionPhase.AFTER_SUCCESS) DbConfigValueChangedEvent event) {
try {
// could be filtered by custom qualifier:
Set<Bean<?>> beans = beanManager.getBeans(Object.class,new AnnotationLiteral<Any>() {});
for (Bean<?> bean : beans) {
Set<InjectionPoint> points = bean.getInjectionPoints();
// What now? javax.enterprise.inject.spi.Bean is the
// bean-representation only.
// Can I somehow resolve the actual bean-instances here?
// Then update Field via Reflection?
}
}
catch(Exception e){
// ...
}
}
}
I also considered DeltaSpike which has some methods for injection-control. However, I did only find methods to inject into new bean instances, or even with new- or null-CreationalContexts (beans not CDI-managed afterwards)
Please note: I am aware that I can solve this specific use-case by injecting the configuration and explicitly getting the current values on each request like this:
public class MyBean {
#Inject
Configuration config;
public void someMethod(){
String host = config.getConfig(MyConfig.HOST);
// ...
}
}
However, I am wondering about the question in general: is there any support for re-injection? Or if not, do the specs (CDI or Java EE) forbid it?
Depending on how fast/slow your db is, this may be expensive. You could probably leverage some cacheing mechanism in the producer method.
Leverage on Instance injection mechanims, which lazily loads the actual injected bean.
Your Producer (Probably leveraging on some of cache to avoid db calls all the tome)
#Singleton
#Lock(LockType.READ)
public class Configuration {
#Produces
#RequestScoped //May fail if not in a request-context, but for ejb-calls, it is guaranteed to work as CDI has EJB Request Context
#DbConfigValue
public String getDbConfigValue(InjectionPoint point) {
// get key for the config-value from qualifier-annotation of the injected field
String key = point.getAnnotated().getAnnotation(DbConfigValue.class).value();
// i have read+cached database config values in #PostConstruct before
return cachedConfigValues.get(key);
}
}
And the injection points:
#SessionScoped
public class MyBean {
#Inject
#DbConfigValue(MyConfig.HOST)
private Instance<String> host;
public void doSomething() {
String myHost = host.get(); // of course will throw exception if value is failing. It will be resolved with every request.
}
}
I am used to Spring on Tomcat/Jetty and I now work on an existing JAX-RS project running on WildFly (RESTEasy).
I would like to know where do the application/deployment property files go on WildFly, standalone/configuration/myapp.properties?
Then how does the application load them? I tried in our class extending javax.ws.rs.core.Application:
#javax.ws.rs.ApplicationPath("")
public class ApplicationConfig extends Application {
#Override
public Map<String, Object> getProperties() {
System.out.println(">>>>>>>>>>>>>>>> get properties");
// I added this method but nothing is printed...
}
#Override
public Set<Class<?>> getClasses() {
System.out.println(">>>>>>>>>>>>>>>> get classes");
// This is printed
...
// classes are loaded correctly
}
}
Then how would I access the properties in the controllers? By the way we don't use dependency injection.
Thanks!
Some Investigation...
Normally what should work
The getProperties() should be called on startup to load any required application properties.
You should be able to inject javax.ws.rs.core.Configuration into your resource classes (with #Context) and retrieve properties through that object. This is stated in the javadoc
This interface can be injected using the Context annotation.
Test
#ApplicationPath("/api")
public class RestApplication extends Application {
#Override
public Map<String, Object> getProperties() {
System.out.println(">>>>>>>>>>>>>>>> get properties");
Map<String, Object> props = new HashMap<>();
props.put("message", "Hello Configuration Properties!");
return props;
}
}
#Path("config")
public class ConfigResource {
#Context
private Configuration configuration;
#GET
public Response getProperty(#QueryParam("prop") String prop) {
String propValue = (String)configuration.getProperty(prop);
return Response.ok(propValue).build();
}
}
Discoveries
The above doesn't work from what I tested with Resteasy 3.0.9.Final. I get some error about no context for this type. I don't know why. Might be a bug, I don't know. Maybe something you can look into.
The above works fine with Jersey 2.16
What works with Resteasy
What I could get to work with Resteasy is to inject Application (as mentioned here into the resource (also with #Context) and get the properties that way.
#Path("config")
public class ConfigResource {
#Context
Application application;
#GET
public Response getProperty(#QueryParam("prop") String prop) {
String propValue = (String)application.getProperties().get(prop);
return Response.ok(propValue).build();
}
}
I just want to create a Portlet and to use an EJB in this Portlet.
Iam using JBoss 7.1 and Liferay 6.2.
I create a EJB project and a liferay plugin project.
I just want to call a method from the EJB, shown here:
#Local
public class PortletController {
public PortletController() {
}
public String getUserName() {
return "foobar";
}
}
My portlet tries to get the username, shown here:
public class ABPortlet extends MVCPortlet {
private String userName;
#EJB
PortletController controller;
public ABPortlet() {}
public void doView(RenderRequest rr, RenderResponse rp) throws IOException, PortletException {
userName = controller.getUserName();
if(userName==null) {
userName = "nope";
}
rr.setAttribute("userName", userName);
super.doView(rr, rp);
}
}
Have I already done something wrong? I read in a tutorial that i can access a local bean without lookup if the bean runs in the same JRE like the portlet.
How do i deploy both projects correctly? I exported the EJB project as jar and added it as dependency to the portlet project, but I just got a NullpointerException in the doView methode, at this line:
userName = controller.getUserName();
I read in a tutorial that i can access a local bean without lookup if the bean runs in the same JRE like the portlet.
You meant in the same JVM when you mentioned the JRE, and yes that is right you don't have to bother about JNDI lookups if your portlet and ejb module runs both locally in the same JVM instance.
Regarding the way you packaged your application, I will advice not to do so (ejb and portlet in the same jar) because it is known from best practices that you would better separate your business module (ejb) from you view module (portlet).
So you may need to package each separately, the portlet goes to a war archive and your ejb to its own jar/module one.
Now going back to your code, you have some things to review following enterprise archive coding conventions:
Use a POJI to declare your local bean skeleton:
#Local
public interface PortletControllerLocal
{
public String getUserName();
}
Implement your Stateless/Stateful session bean and do specify its name annotation property:
#Statefull
#EJB(name = "portletControllerBean")
public class PortletControllerBean implements PortletConrollerLocal
{
public String getUsername()
{
//Do you stuff
}
}
Inject your bean under your portlet controller class with a beanName property:
public class ABPortlet extends MVCPortlet
{
private String userName;
#EJB(beanName = "portletControllerBean")
PortletControllerBean controller;
public ABPortlet() {}
public void doView(RenderRequest rr, RenderResponse rp) throws IOException,PortletException
{
userName = controller.getUserName();
if(userName==null) {
userName = "nope";
}
rr.setAttribute("userName", userName);
super.doView(rr, rp);
}
}
Based on parameters passed to a method, I need to select from one of many Spring beans that are implementations of the same class, but configured with different parameters.
E.g. if user A invokes the method, I need to call dooFoo() on bean A, but if it's user B then I need to call the very same method, only on bean B.
Is there a 'Springier' way of doing this other than sticking all the beans in a map, and deriving a key from the parameters passed to my method?
We face that issue in our project, and we solve it through a Factory-Like class. The client class -the one that needed the bean at runtime- had an instance of the factory, that was injected through Spring:
#Component
public class ImTheClient{
#Autowired
private ImTheFactory factory;
public void doSomething(
Parameters parameters) throws Exception{
IWantThis theInstance = factory.getInstance(parameters);
}
}
So, the IWantThis instance depends on the runtime value of the parameters parameter. The Factory implementation goes like this:
#Component
public class ImTheFactoryImpl implements
ImTheFactory {
#Autowired
private IWantThisBadly anInstance;
#Autowired
private IAlsoWantThis anotherInstance;
#Override
public IWantThis getInstance(Parameters parameters) {
if (parameters.equals(Parameters.THIS)) {
return anInstance;
}
if (parameters.equals(Parameters.THAT)) {
return anotherInstance;
}
return null;
}
}
So, the factory instance holds reference to both of the posible values of the IWantThis class, being IWantThisBadly and IAlsoWantThis both implementations of IWantThis.
Seems like do you want a ServiceLocator using the application context as registry.
See ServiceLocatorFactoryBean support class for creating ServiceLocators mapping keys to bean names without coupling client code to Spring.
Other option is to use a naming convention or annotation based configuration.
for example, assuming that you annotate Services with #ExampleAnnotation("someId"), you can use something like the following Service Locator to retrieve them.
public class AnnotationServiceLocator implements ServiceLocator {
#Autowired
private ApplicationContext context;
private Map<String, Service> services;
public Service getService(String id) {
checkServices();
return services.get(id);
}
private void checkServices() {
if (services == null) {
services = new HashMap<String, Service>();
Map<String, Object> beans = context.getBeansWithAnnotation(ExampleAnnotation.class);
for (Object bean : beans.values()) {
ExampleAnnotation ann = bean.getClass().getAnnotation(ExampleAnnotation.class);
services.put(ann.value(), (Service) bean);
}
}
}
}
Sticking them in a map sounds fine. If it's a Spring-managed map (using util:map, or in Java config), that's better than creating it somewhere else, because then Spring owns all the object references and can manage their lifecycle properly.
If the beans (A, B) you are talking about are SessionScope its no problem at all, they will be selected correctly.
public class BusinessLogic {
private BaseClassOfBeanAandB bean;
public void methodCalledByUserAorB() {
bean.doFoo();
}
}