I'm trying to create a hazelcast-spring application with listeners but with no success for now. When i try to autowire my UserListener is null.
HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance();
IQueue<Object> queue1 = hazelcastInstance.getQueue("q");
queue1.addItemListener(new UserListener(), true);
}
This is my main method and it's working ok when i add the item listener with new instance of UserListener class (my listener) but i need to use the spring autowired annotation because in the listener i have service->dao and i want everything to be autowired.Any ideas how can i accomplish this?
This is my whole code:
Main Class:
public static void main(String[] args) {
HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance();
IQueue<Object> queue1 = hazelcastInstance.getQueue("q");
queue1.addItemListener(new UserListener(), true);
}
Listener Class:
#Component
public class UserListener implements ItemListener<Object> {
#Autowired
private UserService userService;
public void itemAdded(ItemEvent<Object> arg0) {
if (arg0 != null) {
try {
userService.process(arg0);
} catch (SQLException e) {
e.printStackTrace();
}
} else {
System.out.println("null");
}
}
public void itemRemoved(ItemEvent<Object> item) {
System.out.println("The item is removed succesfully");
}
}
POM.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>hazelcastServer</groupId>
<artifactId>hazelcastServer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>3.12.9</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
</dependencies>
</project>
This part of your code
queue1.addItemListener(new UserListener(), true);
creates a new UserListener object bypassing Spring, so any fields annotated as #Autowired don't get instantiated.
You need to try
UserListener userListener = applicationContext.getBean(UserListener.class)
queue1.addItemListener(userListener, true);
It's probably worth checking if userListener object is null, as it might be the Spring component scanning hasn't found you class and created a #Bean, as component scanning can be selective on which packages it scans.
Related
I'm new to Couchbase and Spring Data Couchbase and am having issues getting a simple find query to work. I am running the community edition of couchbase (version 6.6.0), have the latest version of Spring Data Couchbase, and have created a small spring boot app to query from a new bucket I created. I am able to connect to the database ok, but none of the find queries I am using return any data. I have gone into the bucket and added a few indexes, but that didn't seem to help either. I am hoping it is just something small that I am missing.
Here is my code...
Config
#Configuration
#EnableCouchbaseRepositories
public class Config extends AbstractCouchbaseConfiguration {
#Override
public String getConnectionString() {
return "couchbase://127.0.0.1";
}
#Override
public String getUserName() {
return "Administrator";
}
#Override
public String getPassword() {
return "admin1";
}
#Override
public String getBucketName() {
return "testing";
}
#Override
protected boolean autoIndexCreation() {
return true;
}
}
Model
#Document
public class UserInfo {
#Id
private String id;
#QueryIndexed
private String name;
public String getId() {
return id;
}
public String getName() {
return name;
}
}
Repository
#Repository
public interface UserRepository extends CrudRepository<UserInfo, String> {
List<UserInfo> findByName(String name);
}
Main code that runs on app startup. I never seem to be getting data back, even though I have a few documents in my bucket.
#Configuration
public class StartupConfig {
#Autowired
public TravelRepository travelRepository;
#Autowired
public UserRepository userRepository;
#PostConstruct
public void postConstruct() {
System.out.println("Processing data...");
List<UserInfo> userInfo = userRepository.findByName("Steve");
//List<NameInfo> nameInfo = nameRepository.findAll();
if (userInfo.size() == 0) {
System.out.println("No data found");
} else {
System.out.println("Data found: " + userInfo.get(0).getName());
}
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>couchbase</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>couchbase</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-couchbase</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
OK, I figured out the issue. I was manually inserting data via the Admin console, and I didn't realize that you have to add an _class attribute to your documents. When Spring Data Couchbase generates its queries, they will look something like this:
Select * from testing where name = "Steve" and _class= "com.example.couchbase.model.UserInfo";
And it won't find any data if the _class attribute is missing (unless there is some property setting or something that lets you get around this).
I am attempting to connect to a database using JDBC template and have a application.config file with the following:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/herosighting<br>
spring.datasource.username=root<br>
spring.datasource.password=<br>
I do know that this application.properties is being read as I tried changing the server.port and it did work.
Now I have the following code:
#Repository
public class LocationDaoImpl implements LocationDao
{
#Autowired
JdbcTemplate jdbc;
#Override
public Location addLocation(Location l) {
// TODO Auto-generated method stub
return null;
}
#Override
public void updateLocation(Location l) {
// TODO Auto-generated method stub
}
#Override
public List<Location> getAllLocations() {
// TODO Auto-generated method stub
return null;
}
#Override
public void deleteLocationById(int id) {
// TODO Auto-generated method stub
}
#Override
public List<Location> heroLocationsById(int heroId) {
// TODO Auto-generated method stub
return null;
}
#Override
public List<Location> sightingsOnDate(Date date) {
// TODO Auto-generated method stub
return null;
}
#Override
public Location getLocationById(int id)
{
final String SELECT_ROOM_BY_ID = "SELECT * FROM location WHERE id = ?";
return jdbc.queryForObject(SELECT_ROOM_BY_ID, new LocationMapper(), id);//returns room Object
}
}
The return in the last method is returning null becuase the JDBCTemplate is null.
I called the method in main to test it like so:
#Repository
public class Test {
#Autowired
static LocationDao dao;
public static void main(String[] args)
{
Location l = dao.getLocationById(1);
}
}
The interesting thing is, to my knowledge I had the exact same configuration in another file with exact same pom.xml and that project the jdbc worked
My pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.sg</groupId>
<artifactId>SuperheroSightings</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SuperheroSightings</name>
<description>Full stack Spring application for Superhero Sightings</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
EDIT:As there was confusion with my question, I do have a main where i intalize the spring application and this works fine:
#SpringBootApplication
public class SuperheroSightingsApplication
{
public static void main(String[] args)
{
SpringApplication.run(SuperheroSightingsApplication.class, args);
}
}
I am not trying to run unit tests or anything. All I am trying to do is run a method from my DAO to see if the connection to the database is working. The issue is the JDBCTemplate is null and I cannot figure out why.
Here is the exact error:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.sg.SuperheroSightings.Dao.LocationDao.getLocationById(int)" because "com.sg.SuperheroSightings.Test.dao" is null
at com.sg.SuperheroSightings.Test.main(Test.java:18)
This is not how a main method from a Spring Boot Application should look like.
It should be something like this to startup correctly:
public static void main(String[] args)
{
SpringApplication.run(Test.class, args);
}
I have the following problem. I trying to start a Spring Boot application with the DB2 database with Hybernate. So i created a repository and used the #Autowired annotation to get some data from the DB. The problem is that when I run the application i receive the following error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field studenteRepository in com.ibm.snam.ai4legal.controller.HelloWorldController required a bean of type 'com.ibm.snam.ai4legal.repositories.StudenteRepository' that could not be found.
Action:
Consider defining a bean of type 'com.ibm.snam.ai4legal.repositories.StudenteRepository' in your configuration.
Here are the classes of the application
Application class:
package com.ibm.snam.ai4legal.application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication(scanBasePackages = {"com.ibm"})
public class SBApplication {
public static void main(String[] args) {
SpringApplication.run(SBApplication.class, args);
}
}
Repository class:
package com.ibm.snam.ai4legal.repositories;
import org.springframework.data.repository.CrudRepository;
import com.ibm.snam.ai4legal.model.Studente;
public interface StudenteRepository extends CrudRepository<Studente, Integer>{
}
Model class:
package com.ibm.snam.ai4legal.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
#Entity
public class Studente{
#Id
#GeneratedValue
private int id;
private String nome;
private String cognome;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getnome() {
return nome;
}
public void setnome(String nome) {
this.nome = nome;
}
public String getcognome() {
return cognome;
}
public void setcognome(String cognome) {
this.cognome = cognome;
}
}
Controller class:
package com.ibm.snam.ai4legal.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import com.ibm.snam.ai4legal.repositories.StudenteRepository;
#RestController
public class HelloWorldController {
#Autowired
StudenteRepository studenteRepository;
#GetMapping(value = "/home")
public ModelAndView helloworld() {
ModelAndView hello = new ModelAndView("helloworld");
return hello;
}
}
and here the pom.xml file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>projects</groupId>
<artifactId>springwebapp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.ibm.db2.jcc</groupId>
<artifactId>db2jcc4</artifactId>
<version>4.26.14</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
</dependency>
</dependencies>
<!-- <dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>-->
<repositories>
<repository>
<id>repo</id>
<url>file://${project.basedir}/lib</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<packaging>war</packaging>
</project>
On the internet I found that I should insert <context:annotation-config/> in some configuration file but I have no idea in which file I have to put it. Someone can help?
You have to use #ComponentScan annotation. Try the below code.
#ComponentScan({"com.ibm.*"})
#SpringBootApplication
public class SBApplication {
public static void main(String[] args) {
SpringApplication.run(SBApplication.class, args);
}
}
Also mention #Repository annotation in StudenteRepository class.
Either move SBApplication to com.ibm.snam.ai4legal package so it can benefit from default component scanning or add the following annotations to specify packages to be scanned for entities and repositories.
#SpringBootApplication(scanBasePackages = {"com.ibm"})
#EnableJpaRepositories(basePackages = {"com.ibm"})
#EntityScan(basePackages = {"com.ibm"})
public class SBApplication {
public static void main(String[] args) {
SpringApplication.run(SBApplication.class, args);
}
}
Since you are using spring-boot-starter-data-jpa you need to provide the annotation #EnableJpaRepositories to tell springboot to autoconfigure everything.So you might want to use the auto configuration feature of springboot.The #EnableJpaRepositories annotation is not mandatory for auto configuring the spring-data-jpa but in some cases if spring component scan didn't recognize spring-data-jpa in classpath you will have to use this annotation to tell spring to autoconfigure it.
#EnableJpaRepositories will enabling auto configuration support for Spring Data JPA required to know the path of the JPA the repositories. By default, it will scan only the main application package and its sub packages for detecting the JPA repositories.So take care to put the main application class at the root package of your application.
#EnableJpaRepositories(basePackages ="com.ibm")
#SpringBootApplication(scanBasePackages = {"com.ibm"})
public class SBApplication {
public static void main(String[] args) {
SpringApplication.run(SBApplication.class, args);
}
}
Also, if your entity classes are not in the same package then you can use the #EntityScan annotation to specify the base packages. In your case you have not specifies the #Repository annotation on your interface which will tell spring-boot to create default implementations for your interface.If that annotation is not provided then spring will just ignore the interface and the bean creation will not happen.You won't be able to autowire it .So provide that and have methods declared in your interface and spring-bot will take care of the rest.
#Repository
public interface StudenteRepository extends CrudRepository<Studente, Integer>{
//If to find a student record by the id attribute
public Studente findById();
}
I have one framework using Spring Boot which contains a controller RestController class ,
#RequestMapping("/details")
#RestController
public class DataController {
private KafkaStreams kafkaStreams;
public DataController(KafkaStreams kafkaStreams) {
this.kafkaStreams = kafkaStreams;
}
#Autowired
DataService dataService;
#RequestMapping(value = "getAllDetails", method = RequestMethod.GET)
public boolean getAllDetails(KafkaStreams kafkaStreams) {
return ktableService.getAllDetails(kafkaStreams);
}
}
In my service implementation class I am using this kafkaStreams object to find the details for my different services.
Now I am using this framework as a dependency in my one of the other application where I have a runner class,
import org.apache.kafka.streams.KafkaStreams;
#Component
public class PipelineRunner {
private final StreamsBuilder streamsBuilder;
private final KafkaProperties kafkaProperties;
private final SerdesExt serdesExt;
#Autowired
public PipelineRunner(StreamsBuilder streamsBuilder, KafkaProperties kafkaProperties, SerdesExt serdesExt) {
this.streamsBuilder = streamsBuilder;
this.kafkaProperties = kafkaProperties;
this.serdesExt = serdesExt;
}
#PostConstruct
public void run() {
ReflectData.AllowNull.get().addStringable(Utf8.class);
ReflectData.get().addStringable(Utf8.class);
DataProcessor processor = new DataProcessor(streamsBuilder, kafkaProperties,
serdesExt);
start();
}
private void start() {
KafkaStreams kafkaStreams = new KafkaStreams(streamsBuilder.build(),
kafkaProperties.getKafkaStreamsProperties(serdesExt));
System.out.println("----Its is started----");
DataController controller = new DataController(kafkaStreams);
kafkaStreams.start();
}
}
In this class i am trying to create the object for DataController .
So when I am trying to run the application class,
#SpringBootApplication(scanBasePackages = { "framework package" })
#EnableConfigurationProperties(KafkaProperties.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I am getting this error,
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in frameworkpackage.controllers.DataController required a bean of type 'org.apache.kafka.streams.KafkaStreams' that could not be found.
Action:
Consider defining a bean of type 'org.apache.kafka.streams.KafkaStreams' in your configuration.
I am new to Spring Boot. So I might be doing something wrong here. If more info is needed I can provide.
UPDATE
My pom file,
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
</parent>
<properties>
<confluent.version>4.1.0</confluent.version>
<kafka.version>1.1.0</kafka.version>
<lombok.version>1.18.0</lombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.confluent</groupId>
<artifactId>kafka-avro-serializer</artifactId>
<version>${confluent.version}</version>
</dependency>
<dependency>
<groupId>io.confluent</groupId>
<artifactId>kafka-streams-avro-serde</artifactId>
<version>${confluent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-streams</artifactId>
<version>${kafka.version}</version>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>${kafka.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-avro</artifactId>
<version>2.8.5</version>
</dependency>
</dependencies>
A problem is that you are trying to use a bean kafkaStreams in your class DataController, but there is no bean with this name in Spring context. You need to create it manually, so you can autowire it later.
In your case I would suggest to update PipelineRunner.java like this:
import javax.annotation.PostConstruct;
import org.apache.kafka.streams.KafkaStreams;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
#Component
public class PipelineRunner
{
private final StreamsBuilder streamsBuilder;
private final KafkaProperties kafkaProperties;
private final SerdesExt serdesExt;
#Autowired
public PipelineRunner(StreamsBuilder streamsBuilder, KafkaProperties kafkaProperties, SerdesExt serdesExt)
{
this.streamsBuilder = streamsBuilder;
this.kafkaProperties = kafkaProperties;
this.serdesExt = serdesExt;
}
#PostConstruct
public void run()
{
ReflectData.AllowNull.get().addStringable(Utf8.class);
ReflectData.get().addStringable(Utf8.class);
DataProcessor processor = new DataProcessor(streamsBuilder, kafkaProperties,
serdesExt);
start();
}
#Bean
KafkaStreams kafkaStreams()
{
KafkaStreams kafkaStreams = new KafkaStreams(
streamsBuilder.build(),
kafkaProperties.getKafkaStreamsProperties(serdesExt));
System.out.println("----Its is started----");
kafkaStreams.start();
return kafkaStreams;
}
}
You dont need to create an instance of DataController by yourself, this will be done automatically by Spring.
More information about Spring approach to beans is available there
Is it true to say that in the code bellow, the Hazelcast instance will be application scoped ?
#ApplicationScoped
public class Producer {
private HazelcastInstance instance;
#PostConstruct
public void afterCreate() {
instance = Hazelcast.newHazelcastInstance();
}
#Produces
public HazelcastInstance getInstance() {
return instance;
}
}
EDIT
This solution:
Ensure produced been are application scoped.
Provide graceful Hazelcast shut down.
#ApplicationScoped
public class Producer {
private HazelcastInstance instance;
private Producer() {}
#PostConstruct
public void afterCreate() {
instance = Hazelcast.newHazelcastInstance();
}
#PreDestroy
public void beforeDestroy() {
if(instance != null) {
instance.shutdown();
}
}
#Produces
#ApplicationScoped
public HazelcastInstance getInstance() {
return instance;
}
}
You Producer bean will be application scoped, that much is clear.
However, HazelnutInstance bean will be #Dependent.
Therefore, if you, somewhere in your code, do #Inject HazelnutInstance it will inject a dependent instance from CDI viewpoint.
But you always return the same instance, never create new object in your producer, so theoretically, you are sharing that one instance.
However, look out for bean lifecycles! #Dependent beans will be destroyed when the bean where they were injected is destroyed. So now assume it comes to such destruction attempt - Weld will try to destroy your dependent bean and will call #PreDestroy (on "classic" beans) or #Disposes (on beans with producers) method on it.
Therefore, in your case, if there is a disposer method somewhere, which handles HazelcastInstance, that might cause a trouble as it would be invoked every time Weld attempts to destroy/dispose of that dependent bean.
IMO you would be better off if you make HazelcastInstance application scoped. E.g.
#Produces
#ApplicationScoped
public HazelcastInstance getInstance() {
return instance;
}
I wanted to get a unit test prove the expected behaviors.
Code and results can be found below.
Environment
UT launched against WebLogic 12.2.1 remote container using arquillian
1. ApplicationScoped class and producer, new instance: PASS
As expected the test pass: the produced bean is application scoped, and as such the same instance is injected.
#ApplicationScoped
public class Producer {
private Producer() {}
#Produces
#ApplicationScoped
public HazelcastInstance getInstance() {
return Hazelcast.newHazelcastInstance();
}
}
2. ApplicationScoped class and Dependent producer, new instance: FAIL
As expected the test fail: the produced bean being #Dependent, a different instance is injected. (Note: #Dependent being the default scope, it can or not be present, the same result will be the same).
#ApplicationScoped
public class Producer {
private Producer() {}
#Produces
public HazelcastInstance getInstance() {
return Hazelcast.newHazelcastInstance();
}
}
3. ApplicationScoped class and Dependent producer, post construct instance: PASS
As expected the test pass: the produced instance bean is #Dependent. However, its lifecycle is indirectly bound to the Producer lifecycle (through #PostConstruct called once) and as such, the same instance is returned at each call, and dereferenced when the Producer bean is destructed.
Note: Hazelcast hasn't been gracefully shut down in this implementation.
#ApplicationScoped
public class Producer {
private HazelcastInstance instance;
private Producer() {}
#PostConstruct
public void afterCreate() {
instance = Hazelcast.newHazelcastInstance();
}
#Produces
public HazelcastInstance getInstance() {
return instance;
}
}
4. ApplicationScoped class and ApplicationScoped producer, post construct instance: PASS
As expected the test pass: the produced bean is #ApplicationScoped and the same instance is returned during the application life cycle.
#ApplicationScoped
public class Producer {
private HazelcastInstance instance;
private Producer() {}
#PostConstruct
public void afterCreate() {
instance = Hazelcast.newHazelcastInstance();
}
#Produces
#ApplicationScoped
public HazelcastInstance getInstance() {
return instance;
}
}
5. Dependent class and ApplicationScoped producer, post construct instance: PASS
This result is surprising: Producer is annotated #Dependent, its producer #ApplicationScoped and the result is that the same instance is returned by each Producer instance.
Reason: CDI use scoped contexts
#Dependent
public class Producer {
private HazelcastInstance instance;
private Producer() {}
#PostConstruct
public void afterCreate() {
instance = Hazelcast.newHazelcastInstance();
}
#Produces
#ApplicationScoped
public HazelcastInstance getInstance() {
return instance;
}
}
6. Dependent class and producer, post construct instance: FAIL
As expected the test fail: the produced bean being #Dependent, a different instance is injected. (Note: #Dependent being the default scope, it can or not be present, the same result will be the same) as each Producer instance will invoke its own afterCreate method, leading to different instance instances
#Dependent
public class Producer {
private HazelcastInstance instance;
private Producer() {}
#PostConstruct
public void afterCreate() {
instance = Hazelcast.newHazelcastInstance();
}
#Produces
public HazelcastInstance getInstance() {
return instance;
}
}
Unit test
#RunWith(Arquillian.class)
public class ProducerTest {
#Deployment
public static WebArchive createDeployment() {
// Import Maven runtime dependencies
File[] files = Maven.resolver()
.loadPomFromFile("pom.xml")
.importRuntimeDependencies()
.resolve()
.withTransitivity()
.asFile();
// Create deploy file
WebArchive war = ShrinkWrap.create(WebArchive.class, ProducerTest.class.getName() + ".war")
.addClass(Producer.class)
.addClass(ProducerTest.class)
.addAsLibraries(files);
// Show the deploy structure
System.out.println(war.toString(true));
return war;
}
#Inject
private HazelcastInstance hzInstance1;
#Inject
private HazelcastInstance hzInstance2;
#Before
#After
public void cleanup() {
Hazelcast.shutdownAll();
assertEquals(emptySet(), getAllHazelcastInstances());
}
#Test
public void producerTest() {
assertTrue(hzInstance1.equals(hzInstance2));
}
}
arquillian.xml
<?xml version="1.0"?>
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://jboss.org/schema/arquillian"
xsi:schemaLocation="http://jboss.org/schema/arquillian http://www.jboss.org/schema/arquillian/arquillian_1_0.xsd">
<container qualifier="wls-remote" default="true">
<configuration>
<property name="wlHome">PATH_TO_WLSERVER_DIRECTORY</property>
<property name="adminUrl">ADMIN_URL</property>
<property name="adminUserName">USER_NAME</property>
<property name="adminPassword">PASSWORD</property>
<property name="target">TARGET_SERVER</property>
</configuration>
</container>
</arquillian>
hazelcast.xml
<?xml version="1.0" encoding="UTF-8"?>
<hazelcast
xsi:schemaLocation="http://www.hazelcast.com/schema/config
http://www.hazelcast.com/schema/config/hazelcast-config-3.5.xsd"
xmlns="http://www.hazelcast.com/schema/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<group>
<name>producer</name>
<password>producer_pass</password>
</group>
<network>
<port auto-increment="false">10710</port>
<join>
<multicast enabled="false" />
<tcp-ip enabled="true">
<member>localhost</member>
</tcp-ip>
</join>
</network>
</hazelcast>
For the sake of the completeness, the POM
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>HeyStackExchange</groupId>
<artifactId>cdi-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<url>https://stackoverflow.com/questions/46559523/scoped-instance</url>
<name>Arquillian WLS 12.1.x/12.2.x - CDI Example</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven-compiler-plugin.version>3.6.1</maven-compiler-plugin.version>
<arquillian.version>1.1.5.Final</arquillian.version>
<!-- JEE -->
<jboss-javaee-7.0.version>1.1.0.Final</jboss-javaee-7.0.version>
<!-- Arquillian -->
<arquillian-wls-12.1.x.version>1.0.1.Final</arquillian-wls-12.1.x.version>
<!-- Cache -->
<hazelcast.version>3.8.3</hazelcast.version>
<!-- UT -->
<junit.version>4.8.2</junit.version>
<!-- Logging -->
<slf4j-api.version>1.7.25</slf4j-api.version>
<slf4j-log4j12.version>1.7.9</slf4j-log4j12.version>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-bom</artifactId>
<version>${arquillian.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- JEE -->
<dependency>
<groupId>org.jboss.spec</groupId>
<artifactId>jboss-javaee-7.0</artifactId>
<version>${jboss-javaee-7.0.version}</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
<!-- Cache -->
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>${hazelcast.version}</version>
</dependency>
<!-- Arquillian -->
<dependency>
<groupId>org.jboss.shrinkwrap.resolver</groupId>
<artifactId>shrinkwrap-resolver-impl-maven</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.container</groupId>
<artifactId>arquillian-wls-remote-12.1.x</artifactId>
<!--artifactId>arquillian-wls-managed-12.1.x</artifactId -->
<version>${arquillian-wls-12.1.x.version}</version>
<scope>test</scope>
</dependency>
<!-- UT -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-log4j12.version}</version>
</dependency>
</dependencies>
</project>
Yes the Producer will receive the Scope you applied with the Annotation #ApplicationScoped. This will result in only having one instance for the whole CDI Application. Every Request to the class Producer will come to the same instance of the class since it will be only initialized once and then kept active. Since the Producer is application scoped the instance of Hazelcastwill not change, as long as you don't change it in the class itself ( e.g setter )
An object which is defined as #ApplicationScoped is created once for the duration of the application.
taken from here