I have a spring-boot project with 3 maven modules in it: producer, consumer and api.
Api contains an interface.
Consumer depends on api and contains the main method and #SpringBootApplication. The class is also in a package that prefixes all the other classes in the other two jars so Component Scan could find everything
Producer depends on api and contains an implementation of the api interface annotated with #Service
In Consumer, i'm trying to get the producer to be injected in the constructor but without referencing the concrete implementation, just the interface. The consumer maven module doesn't even depend on the producer module. This is similar to the way you create applications in OSGi where concrete provider implementations are supposed to be hidden from their consumers.
My problem is that the producer is not being injected. It is not being instantiated or even its class loaded since nobody is referencing it. How can I accomplish this in spring (boot) while keeping the strong encapsulation requirement of consumers not being aware of concrete producers?
When I run the app I get UnsatisfiedDependencyException since there's not producer instantiated to be injected
This is a simplified representation of my code
package com.foo.api
public interface Doer {
}
===== different jar =====
package com.foo
#SpringBootApplication
public class Consumer {
public static void main(String[] args) {
SpringApplication.run(Consumer.class, args);
}
#Autowire
public Consumer(Doer someDoer) {
}
}
===== different jar ======
package com.foo.services
#Service
public class Producer implements Doer {
}
You need to add the Producer module as a dependency of the Consumer module, otherwise maven is not packaging it with the spring boot application - hence it is not available at runtime (the jar is missing).
In order to keep the good separation you defined, make sure to set the dependency's scope to runtime. This means it is not required in compile time but maven knows it needs to be in runtime so it packages the dependency with the application.
For example, add the following the consumer's pom.xml (after setting the correct values...):
<dependency>
<groupId>com.foo.services</groupId>
<artifactId>producer</artifactId>
<version>1.2.3</version>
<scope>runtime</scope>
</dependency>
Related
I'm using Spring Batch with #EnableBatchProcessing(modular = true)
The problem is that in this mode you have to explicitly declare which beans to initialize (i.e which classes Spring needs to scan)
Here's an exmaple:
#Configuration
#EnableBatchProcessing(modular = true)
public class ModularJobsConfig {
#Autowired
private AutomaticJobRegistrar registrar;
#PostConstruct
public void initialize() {
registrar.addApplicationContextFactory(new GenericApplicationContextFactory(
SomeJobConfig.class,
SomeJobTasklet.class,
SomeClassToDefineTaskExecutor.class
SomeClassToRunTheJob.class));
}
}
I can imagine that by the time I'll have several jobs, this configuration class will be bloated. How can I automate this?
It is worth mentioning that each job has its own package (e.g com.example.jobs.<job_name>) + they are defined in different maven-modules but I think it's irrelevant.
Further Clarification
I have a core module which contains the configuration above. Every job is defined in a separate maven module and it's been registered as a maven dependency in core.
Mainly for preventing naming clash, I'm using #EnableBatchProcessing(modular = true) and I'm registering the jobs with AutomaticJobRegistrar as you can see in the example code above.
Ideally, I'd like Spring to scan the maven dependency and to do it for me (i.e. defining GenericApplicationContextFactory)
Currently, it's cumbersome to add manually each and every class (in the example above: SomeJobConfig.class, SomeJobTasklet.class etc)
As a counter example, if I didn't use modular=true I could let Spring Batch to load all beans on its own, but then I'd have to make sure methods names are unique across all the modules.
I am looking to do some refactoring of a Java J2EE application, but I am not clear how to have CDI provide the needed dependencies:
The current setup is quite simple/easy to understand:
#ApplicationScoped
public class MyApplication {
#Inject
#Named("Default")
private Dependency dependency;
public void dostuff(){
dependency.process();
}
}
I now need a new instance of dependency each time I call dostuff.
I am unclear on how to use CDI to create this for me. My Dependency has its own dependencies that I would like CDI to create for me.
I expect there is a layer of indirection I need to add.
Additional context:
This class is part of a process that polls for work to be done, and is hosted in Wildfly.
We are not using Spring in the project.
Since what you desire is having a new instance of Dependency, each time the method is called, I think what you need is an instance of Provider that is (javax.inject.Provider<T>) injected in your class/bean.
Inject the provider to your current class:
#Inject Provider<DesiredBean> provider;
Then, in your method doStuff() obtain the new instance:
DesiredBean desiredBean = provider.get();
This should get you going.
Is there a specific reason you need to use CDI besides the dependency injection?
If not, I'd suggest making doStuff() take a Dependency object as a parameter:
public void doStuff(Dependency dependency) {
dependency.process();
}
Then, when you call the method, you provide it with a new instance of Dependency:
myApplication.doStuff(new Dependency());
That way, you are still keeping your class less coupled than declaring a new instance in the constructor or field.
I have a interface which has function used to query ElasticSearch. It extends the ElasticsearchRepository for doing it.
public interface HouseholdRepository extends ElasticsearchRepository<SearchHouseholdESBean, String> {
List<SearchHouseholdESBean> findByPhoneNumberAndActiveInd(String phoneNumber, String activeInd);
The problem is how do i call this in my business class where i need to get the results. This being an interface , i can't create an object of this to call the methods. Also, the implementation is implicit to the jars in the Elastic Search.
To use elastichsearch repositories you must follow the next steps:
1. add annotation #EnableElasticsearchRepositories on your SpringBootApplication
#SpringBootApplication
#EnableElasticsearchRepositories
public class Application {
//...
2. Make sure that the interface HouseholdRepository is scanned by the spring-boot application. You can simple achieve this by placing it under the same root package as your Application class.
3.You will just #Autowire HouseholdRepository in your service without further changes. The idea behind spring boot data is that the code will be generated based on that interface.
OBS: make sure that you have the proper project dependencies. You should depend on spring-boot-starter-data-elasticsearch to avoid extra configuration effort.
According to HK2 #Service javadoc
Annotation placed on classes that are to be automatically added to an
hk2 ServiceLocator.
I don't know how to make ServiceLocator find annotated classes automatically.
TestService
#Contract
public interface TestService {
}
TestServiceImpl
#Service
public class TestServiceImpl implements TestService {
}
Main
public static void main(String[] args) {
ServiceLocator locator = ServiceLocatorUtilities.createAndPopulateServiceLocator();
TestService service = locator.getService(TestServiceImpl.class);
System.out.println(service); // null
}
The result is always null. I have to add Descriptor so the ServiceLocator can find it.
public static void main(String[] args) {
ServiceLocator locator = ServiceLocatorUtilities.createAndPopulateServiceLocator();
DynamicConfigurationService dcs = locator.getService(DynamicConfigurationService.class);
DynamicConfiguration config = dcs.createDynamicConfiguration();
config.bind(BuilderHelper.link(TestServiceImpl.class).to(TestService.class).in(Singleton.class).build());
config.commit();
TestService service = locator.getService(TestServiceImpl.class);
System.out.println(service); // TestServiceImpl instance
}
How do I let ServiceLocator find the annotated classes automatically ? Did I misunderstand something ?
You need to run the hk2-inhabitant-generator over your built classes in order to get automatic detection of services. There is more information here as well.
What that step does in the build process is to create a file named META-INF/hk2-locator/default with information about services. The createAndPopulateServiceLocator call then reads those files and automatically adds those service descriptors into the returned ServiceLocator.
FYI, I was so frustrated with the reliance on the inhabitant files rather than having the capability for runtime scanning of annotated classes, I wrote this project:
https://github.com/VA-CTT/HK2Utilities
Since Eclipse / Maven / inhabitant runtime generators wouldn't play nice, it was nearly impossible to debug code that made use of HK2 in eclipse without runtime scanning.
The HK2Utilities package is available in central:
<dependency>
<groupId>gov.va.oia</groupId>
<artifactId>HK2Utilities</artifactId>
<version>1.4.1</version>
</dependency>
To use it, you just call:
ServiceLocator locator = HK2RuntimeInitializer.init("myName", false, new String[]{"my.package.one", "my.package.two"});
This will scan the runtime classpath for classes in the packages listed, and automatically populate the service locator with them.
You don't ever have to generate inhabitant files with this model - and in practice, I found it to be faster performing than the inhabitant processing code as well (not that the performance matters much for this one-time operation)
---edit---
I still maintain this code - the current release is:
<dependency>
<groupId>net.sagebits</groupId>
<artifactId>HK2Utilities</artifactId>
<version>1.5.2</version>
</dependency>
And the project location is now:
https://github.com/darmbrust/HK2Utilities
Well now (2.6.1) all you need to do is add the dependencies - javax.inject, hk2-utils, hk2-api and hk2-metadata-generator.
When you build the project, javac compiler will generate a 'default' file in META-INF containing the wiring as follows:
[service-class-name]S
contract={contract-class-name}
This will be registered by the ServiceLocator during the run.
This should be sufficient. However if that does not work, there are other options,
mvn plugin
org.glassfish.hk2
hk2-inhabitant-generator
2.5.0-b36
generate-inhabitants
cmd line tool
java org.jvnet.hk2.generator.HabitatGenerator
[--file jarFileOrDirectory]
[--outjar jarFile]
[--locator locatorName]
[--verbose]
More on this https://javaee.github.io/hk2/inhabitant-generator.html
We currently have a project that consists of multiple applications as well as a base library.
Both, the applications as well as the base library contain stateless EJBs and each application might introduce an EJB that inherits from a base library EJBand thus implements the same interface.
A short example:
In the base library we have:
#Stateless
#Local( IUserService.class )
public UserServiceBean implements IUserService {
public void login(String user, String password) {...}
}
In the application we need to override login(...) and thus we have:
#Stateless
#Local( { ISpecificUserService.class, IUserService.class } )
public SpecificUserServiceBean extends UserServiceBean implements ISpecificUserService {
public void login(String user, String password) { ... } //override
}
If I now have another EJB in the application that needs to have a reference to SpecificUserServiceBean I'd do #EJB ISpecificUserService userService;.
However, if there is an EJB in the base library, it would contain #EJB IUserService userService; and here's the problem:
We need to get the specific service in the application be injected in the base library EJB. However, there are two EJBs registered for the same local interface, which means that the container might return the base EJB or the specific EJB.
You could say "Don't add the base library jar as a module in application.xml" but that's not possible right now, since it contains other EJBs that need to be deployed. We could move the EJBs to be overridden to a different jar but since almost every EJB might be overridden, depending on the application, we'd end up with a jar per EJB.
Thus, I'd like to exclude or remove the base EJB from the dependency injection container if there exists a specific override. Since I know that at deploy time, I could also use a configuration file to tell the container not to load a specific EJB class.
I might add a service that modifies the container afterwards but that would be my last resort.
Do you have any ideas on what we could do?
Thanks in advance.
Btw, we're working on JBoss 4.2.3 with EJB 3.0.
The problem is that you're wiring your app partially in the base lib which is bad since you can't override this wiring.
So the solution is to remove #Stateless #Local( IUserService.class ) from UserServiceBean in your base lib; the base lib just provides default implementations for the beans but it must not wire them.
In your apps, you need this code:
#Stateless
#Local( IUserService.class )
public AppUserServiceBean extends UserServiceBean {}
to create the wiring. Move all those beans into a special package which you can copy into each app so you get the default wiring for everything.