How to access application scoped cdi bean packed as jboss shared library - java

I have to use an application scoped cdi bean present in jboss shared libraries in my ear application.
Example:
jboss\modules\com\test\test.jar
In test.jar I have one application scoped bean
#ApplicationScoped
public class Test {
#Inject
SomeClass someClass;
#PostConstructor
public void init() {
someClass.doSomething();
}
public void testMethod() {
}
}
myapp.ear → ejb.jar
In ejb.jar I have one class which initializes Test application scoped bean.
public class Another {
#Inject
Test test;
public void myMethod() {
test.testMethod();
}
}
When I test this example, I'll get null pointer exception as I'm trying to inject application scoped bean which is out side an ear application.
Note: I cannot have test.jar in my ear lib directory, as per my requirement it will be delivered as jboss shared library.
Any ideas how to access application scoped bean?

Add beans.xml to META-INF directory of your test.jar. In this case container will scan test.jar file for CDI beans.
This is also strange that you get NPE. You would get "unsatisfied dependencies" if injected bean was not found. Probably you instantiate Test bean incorrectly. For example, via the New operator.

Related

What happens when we define a bean inside the test folder?

I have defined a class with annotation #Configuration and some bean methods in it. This class is inside the test folder. So when I run the application will these beans be loaded by SpringBoot?

EJB Injection Between EARs on the Same Server

I'm running into an issue trying to migrate an application from Jboss AS6 to Wildfly. The application consists of a 'Product' ear, a 'Person' ear, and a 'Common' jar containing shared interfaces and utilities. All are being deployed as modules on a Wildfly application server. I need to be able to inject a service bean defined in Person into Product. The LoginService bean is located in Person and looks like this:
#Stateless
#Remote(LoginService.class)
public class LoginServiceBean implements LoginService {
#Resource
protected SessionContext context;
}
When I build and deploy the Person ear, I get the following log for my jndi bindings:
java:global/Person/Person-ejb/LoginServiceBean!com.tura.common.service.login.LoginService
java:app/Person-ejb/LoginServiceBean!com.tura.common.service.login.LoginService
java:module/LoginServiceBean!com.tura.common.service.login.LoginService
java:jboss/exported/Person/Person-ejb/LoginServiceBean!com.tura.common.service.login.LoginService
ejb:Person/Person-ejb/LoginServiceBean!com.tura.common.service.login.LoginService
java:global/Person/Person-ejb/LoginServiceBean
java:app/Person-ejb/LoginServiceBean
java:module/LoginServiceBean
In the Product project I then have a stateless EJB bean
#Stateless(name = "ClientServiceProvider")
public class ClientServiceProviderBean implements ClientServiceProvider{
#EJB(name = "ejb:Person/Person-ejb/LoginServiceBean!com.tura.common.service.login.LoginService")
protected LoginService loginService;
}
When I try to deploy, it then fails with the error:
No EJB found with interface of type
'com.tura.common.service.login.LoginService' for binding
ejb:Person/Person-ejb/LoginServiceBean!com.tura.common.service.login.LoginService"
I've tried every binding configuration I can think of; nothing seems to work. All the documentation I can find seems to want to use a manual jndi lookup rather than the annotation. What am I doing wrong here? Is there really no way to inject services between EARs?

Should the service beans defined in the jar library be instantiated by the jar library configuration or by the client war application configuration?

I have a library as a jar packaging Maven project which offers services.
The #Configuration class to instantiate service beans:
#Configuration
public class JpaService {
#Bean
public UserRoleServiceImpl userRoleService() {
return new UserRoleServiceImpl();
}
#Bean
public UserServiceImpl userService() {
return new UserServiceImpl();
}
}
I reckoned I needed to have the beans instantiation outside of the jar archive.
So I had a copy of this class in the test part of the project, and another copy in another war packaging Maven project using the library.
But what if I instantiated the services in the jar library itself. I would need to only do it once, be it for testing or for all client projects using it.
UPDATE: Two questions...
Should all component scanning only be done from the war ? Or should the jar service components be scanned from the jar ?
And what if two components (one in the jar and one in the war) have the same class name in the same package ?
I dont think i fully understand your question, but if you are aiming to add beans to your application context that is outside the jar then what you have to do is use the #ComponentScan annotation, and specify the package you want to scan, the package can be in a different jar, the only thing required is that you anotate the clases you want to include with #Service, #Componenet or even #Configuration
example:
#Configuration
#ComponentScan(basePackages={"com.somepackacge.controller",
...
you can include as much packages as you like.
By the way dont copy your clases from one place to the other, maintining that will be a headache in the futute, if you want to include your configuration in your tests you can always do :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyConfigClass.class)
Where MyConfigClass is the class u used before with the component scan
Hope it helps

Inject bean by CDI from different deployment

This might be a bit complex.
I have an EAR and a WAR deployed in a JBoss 7 container side by side.
The EAR has a service.jar module with EJBs and in its lib folder there is another utility jar. In the utility jar there is a resource producer class, like this:
public class BaseResources {
#Produces
private Logger getLogger(InjectionPoint ip) {
String category = ip.getMember()
.getDeclaringClass()
.getName();
return LoggerFactory.getLogger(category);
}
}
Now the WAR depends on the EAR and sees all its classes.
But when I try to inject a Logger instance into a class in the WAR, there is a org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [Logger] with qualifiers [#Default] at injection point thrown, telling me there is no Logger instance to inject.
Is there a way to inject the Logger into a class in the WAR?

Spring Dependency Injection and Plugin Jar

I have web application running with a default impl of a backend service. One should be able to implement the interface and drop the jar into the plugins folder (which is not in the apps classpath). Once the server is restarted, the idea is to load the new jar into the classloader, and have it take part in dependency injection. I am using Spring DI using #Autowired. The new plugin service impl will have #Primary annotation. So given two impls of the interface, the primary should be loaded.
I got the jar loaded into the classloader and can invoke the impl manually. But I haven't been able to get to to participate in the Dependency Injection, and have it replace the default impl.
Here's a simplified example:
#Controller
public class MyController {
#Autowired
Service service;
}
//default.jar
#Service
DefaultService implements Service {
public void print() {
System.out.println("printing DefaultService.print()");
}
}
//plugin.jar not in classpath yet
#Service
#Primary
MyNewService implements Service {
public void print() {
System.out.println("printing MyNewService.print()");
}
}
//For lack of better place, I loaded the plugin jar from the ContextListener
public class PluginContextLoaderListener extends org.springframework.web.context.ContextLoaderListener {
#Override
protected void customizeContext(ServletContext servletContext,
ConfigurableWebApplicationContext wac) {
System.out.println("Init Plugin");
PluginManager pluginManager = PluginManagerFactory.createPluginManager("plugins");
pluginManager.init();
//Prints the MyNewService.print() method
Service service = (Service) pluginManager.getService("service");
service.print();
}
}
<listener>
<listener-class>com.plugin.PluginContextLoaderListener</listener-class>
</listener>
Even after I have loaded the jar into the classloader, DefaultService is still being injected as service. Any idea how I get the plugin jar to participate into the spring's DI lifecycle?
Edited:
To put it simply, I have a war file that has a few plugin jars in a plugins directory inside the war. Based on a value from a configuration file that the app looks at, when the app is started, I want to load that particular plugin jar and run the application with it. That way, I can distribute the war to anyone, and they can choose which plugin to run based on a config value without having to to repackage everything. This is the problem I am trying to solve.
It seems like all You need is to create the Spring ApplicationContext properly. I think it's possible without classpath mingling. What matters most are the locations of the Spring configuration files within the classpath. So put all Your plugin jar's into WEB-INF/lib and read on.
Let's start with the core module. We'll make it to create it's ApplicationContext from files located at classpath*:META-INF/spring/*-corecontext.xml.
Now we'll make all plugins to have their config files elsewhere. I.e. 'myplugin1' will have its config location like this: classpath*:META-INF/spring/*-myplugin1context.xml. And anotherplugin will have the configs at classpath*:META-INF/spring/*-anotherplugincontext.xml.
What You see is a convension. You can also use subdirectiries if You like:
core: classpath*:META-INF/spring/core/*.xml
myplugin1: classpath*:META-INF/spring/myplugin1/*.xml
anotherplugin: classpath*:META-INF/spring/anotherplugin/*.xml
What matters is that the locations have to be disjoint.
All that remains is to pass the right locations to the ApplicationContext creator. For web applications the right place for this would be to extend the ContextLoaderListener and override the method customizeContext(ServletContext, ConfigurableWebApplicationContext).
All that remains is to read Your config file (its location can be passed as servlet init parameter). Than You need to construct the list of config locations:
String locationPrefix = "classpath*:META-INF/spring/";
String locationSiffix = "/*.xml";
List<String> configLocations = new ArrayList<String>();
configLocations.add(locationPrefix + "core" + locationSiffix);
List<String> pluginsTurnedOn = getPluginsTurnedOnFromConfiguration();
for (String pluginName : pluginsTurnedOn) {
configLocations.add(locationPrefix + pluginName + locationSiffix);
}
applicationContext.setConfigLocations(configLocations.toArray(new String[configLocations.size()]));
This way You can easily manage what is and what is not loaded into Spring ApplicationContext.
Update:
To make it work there's one more hidden assumption I made that I'm about to explain now. The base package of the core module and each plugin should also be disjoint. That is i.e.:
com.mycompany.myapp.core
com.mycompany.myapp.myplugin1
com.mycompany.myapp.anotherplugin
This way each module can use <context:componet-scan /> (on equivalent in JavaConfig) easily to add classpath scanning for it's own classes only. The core module should not contain any package scanning of any plugin packages. The plugins should extend configuration of ApplicationContext to add their own packages to classpath scanning.
If you restart the server, I see no reason why you can't just add the JAR to the WEB-INF/lib and have it in the CLASSPATH. All the complication of a custom class loader and context listener goes away, because you treat it just like any other class under Spring's control.
If you do it this way because you don't want to open or modify a WAR, why not put it in the server /lib directory? Let the server class loader pick it up. This makes all plugin classes available to all deployed apps.
The answer depends on how important the separate /plugin directory is. If it's key to the solution, and you can't add the JAR to the server's /lib directory, then that's that. I've got nothing. But I think it'd be worthwhile to at least revisit the solution you have to make sure that it's the only way to accomplish what you want.

Categories