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;
}
}