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
Related
Whenever I used #MicronautTest in my project, the whole application is started as a whole and because certain things need to be done at startup by services, they are executed also.
I don't need the whole Singleton shebang when I'm testing a simple controller.
My question is how do I limit which controllers and singletons are loaded when testing Micronaut?
I have a small demo example which shows the problem
Pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>micronaut-test</artifactId>
<version>0.1</version>
<packaging>${packaging}</packaging>
<parent>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-parent</artifactId>
<version>2.5.1</version>
</parent>
<properties>
<packaging>jar</packaging>
<jdk.version>11</jdk.version>
<release.version>11</release.version>
<micronaut.version>2.5.1</micronaut.version>
<exec.mainClass>com.example.Application</exec.mainClass>
<micronaut.runtime>netty</micronaut.runtime>
</properties>
<repositories>
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-inject</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-validation</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.micronaut.test</groupId>
<artifactId>micronaut-test-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-http-client</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-http-server-netty</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-runtime</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.micronaut.build</groupId>
<artifactId>micronaut-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<!-- Uncomment to enable incremental compilation -->
<!-- <useIncrementalCompilation>false</useIncrementalCompilation> -->
<annotationProcessorPaths combine.children="append">
</annotationProcessorPaths>
<compilerArgs>
<arg>-Amicronaut.processing.group=com.example</arg>
<arg>-Amicronaut.processing.module=micronaut-test</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.java
package com.example;
import io.micronaut.runtime.Micronaut;
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class, args);
}
}
A simple service that throws whenever it is instantiated to indicate the problem
#Singleton
public class SingletonService {
#EventListener
public void init(ApplicationStartupEvent event) {
throw new RuntimeException("I was initialized");
}
}
A simple controller
#Controller
public class TestController {
#Get
public Simple findAll() {
return new Simple("ATest");
}
public static class Simple {
private final String test;
public Simple(String test) {
this.test = test;
}
public String getTest() {
return test;
}
}
}
And a straightforward test
#MicronautTest
class TestControllerTest {
#Inject
#Client("/")
RxHttpClient rxHttpClient;
#Test
void controller() {
HttpResponse<ByteBuffer> byteBufferHttpResponse = rxHttpClient.exchange("/").blockingFirst();
assert byteBufferHttpResponse.getStatus().getCode() == 200;
}
}
The test results are
[INFO] Running com.example.controller.TestControllerTest
11:01:52.804 [main] INFO i.m.context.env.DefaultEnvironment - Established active environments: [test]
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 7.232 s <<< FAILURE! - in com.example.controller.TestControllerTest
[ERROR] com.example.controller.TestControllerTest Time elapsed: 7.229 s <<< ERROR!
java.lang.RuntimeException: I was initialized
How do I prevent the test from starting that SingletonService? Stubbing is an option but keep in mind that this is a simple demo illustrating the issue for a bigger project. Is there no other straightforward way?
According to the docs of #MicronautTest, it shouldn't be scanning the classpath but it clearly is?
Here are some other configurations I've already attempted without any success:
Adding a ApplicationBuilder that disables eagerInit
#MicronautTest(application = TestControllerTest.TestApplication.class)
class TestControllerTest {
public static class TestApplication {
public static void main(String[] args) {
Micronaut.build(args)
.eagerInitAnnotated(null)
.eagerInitSingletons(false)
.mainClass(TestApplication.class);
}
}
#Inject
#Client("/")
RxHttpClient rxHttpClient;
#Test
void controller() {
HttpResponse<ByteBuffer> byteBufferHttpResponse = rxHttpClient.exchange("/").blockingFirst();
assert byteBufferHttpResponse.getStatus().getCode() == 200;
}
}
Passing along a contextbuilder
#MicronautTest(contextBuilder = TestControllerTest.TestContextBuilder.class)
class TestControllerTest {
public static class TestContextBuilder extends DefaultApplicationContextBuilder {
public TestContextBuilder() {
eagerInitSingletons(false);
eagerInitAnnotated(null);
eagerInitConfiguration(false);
}
}
#Inject
#Client("/")
RxHttpClient rxHttpClient;
#Test
void controller() {
HttpResponse<ByteBuffer> byteBufferHttpResponse = rxHttpClient.exchange("/").blockingFirst();
assert byteBufferHttpResponse.getStatus().getCode() == 200;
}
}
Which all yield the same response.
Hopefully someone knows how to limit the scope of bean instantation with the #MicronautTest or I'll most likely switch back to Spring boot
Thanks in advance
My question is how do I limit which controllers and singletons are
loaded when testing Micronaut?
At least there is the #Requires annotation that allows doing flexible configuration if a bean should be loaded within the current environment.
For example, in your case it should be something like:
import javax.inject.Singleton;
import io.micronaut.context.annotation.Requires;
#Singleton
#Requires(notEnv = {Environment.TEST})
public class SingletonService {
#EventListener
public void init(ApplicationStartupEvent event) {
throw new RuntimeException("I was initialized");
}
}
As a result, the exception from the service is not thrown.
However, I don't really know the purpose why you need to exclude the service. And it could be that other approaches might be more convenient.
For example, see Mocking Collaborators section from the micronaut test documentation: https://micronaut-projects.github.io/micronaut-test/latest/guide/
I'm trying to test that my beans have correct validation annotations. I'm using spring-boot. Here is an example test case:
package com.example.sandbox;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import javax.validation.ConstraintViolationException;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.validation.annotation.Validated;
#SpringBootTest
class ValidationTest {
#Test
void testConstructor() {
TestedBean bean = new TestedBean(null);
assertThatThrownBy(() -> checkIfValidated(bean)).isInstanceOf(ConstraintViolationException.class);
}
#Test
void testSetter() {
TestedBean bean = new TestedBean(null);
assertThatThrownBy(() -> bean.setSomeProperty(null)).isInstanceOf(ConstraintViolationException.class);
}
private void checkIfValidated(#Valid TestedBean bean) {
}
#Validated
class TestedBean {
#NotNull
private String someProperty;
public TestedBean(String someProperty) {
super();
this.someProperty = someProperty;
}
public String getSomeProperty() {
return someProperty;
}
public void setSomeProperty(#NotNull String someProperty) {
this.someProperty = someProperty;
}
}
}
I expect the call to checkIfvalidated() and to setSomeProperty(null) to raise a ConstraintViolationException, and the tests to pass, but they both fail with:
java.lang.AssertionError:
Expecting code to raise a throwable.
at com.example.sandbox.ValidationTest.test(ValidationTest.java:20)
...
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.4.0</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>com.example.springbootsandbox</artifactId>
<version>0.0</version>
<name>SpringBootSandbox</name>
<description>Sandbox for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.5.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Why is there no ConstraintViolationException raised here? The bean property has a #NotNull annotation, the bean itself is #Validated and the method signature requires a #Valid bean.
Is there a simple way to have that exception raised in the context of my test class?
When I use validation annotations on method signatures for a service interface, everything works as expected. I don't understand where is the difference.
Service interface:
package com.example.sandbox;
import javax.validation.constraints.NotNull;
import org.springframework.validation.annotation.Validated;
#Validated
public interface IService {
public void setValue(#NotNull String value);
}
Service implementation:
package com.example.sandbox;
import org.springframework.stereotype.Service;
#Service
public class SomeService implements IService {
#Override
public void setValue(String value) {
// Do nothing
}
}
Test case:
package com.example.sandbox;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import javax.validation.ConstraintViolationException;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
#SpringBootTest
class SomeServiceTests {
#Autowired
IService service;
#Test
void testSetValue() {
assertThatThrownBy(() -> service.setValue(null)).isInstanceOf(ConstraintViolationException.class);
}
}
==> The test passes.
Working code according to the given answer:
The test class:
#SpringBootTest
class ValidationTest {
#Autowired
private Validator validator; // Using the default validator to test property annotations
#Autowired
private TestedBeanService service; // Using a service to test method annotations
#Test
void testPropertyAnnotations() {
TestedBean bean = new TestedBean(null);
Set<ConstraintViolation<TestedBean>> violations = validator.validate(bean);
assertThat(violations).isNotEmpty();
}
#Test
void testMethodAnnotations() {
TestedBean bean = new TestedBean(null);
assertThatThrownBy(() -> service.setBeanProperty(bean, null)).isInstanceOf(ConstraintViolationException.class);
}
}
The tested bean:
#Validated
class TestedBean {
#NotNull
private String someProperty;
public TestedBean(String someProperty) {
super();
this.someProperty = someProperty;
}
public String getSomeProperty() {
return someProperty;
}
public void setSomeProperty(String someProperty) { // No more annotation on setter
this.someProperty = someProperty;
}
}
The service interface:
#Validated
public interface TestedBeanService {
// method annotation on the interface method
void setBeanProperty(TestedBean bean, #NotNull String someProperty);
}
The service implementation:
#Service
public class TestedBeanServiceImpl implements TestedBeanService {
#Override
public void setBeanProperty(TestedBean bean, String someProperty) {
bean.setSomeProperty(someProperty);
}
}
Why is there no ConstraintViolationException raised here? The bean property has a #NotNull annotation, the bean itself is #Validated and the method signature requires a #Valid bean.
Annotations by themselves do not mean anything, they should be processed in some way. In this case the #Validated annotation is processed by Spring for its beans. The test is not a Spring bean, so the framework does not look at the annotations related to valdidation, hence no exception.
Even if the test were a Spring Bean, the approach may not work out of the box. See this question for details.
Is there a simple way to have that exception raised in the context of my test class?
Take a look at this question
When I use validation annotations on method signatures for a service interface, everything works as expected. I don't understand where is the difference.
This happens because the service is a Spring bean, but test is not. When a method on the service is invoked, it gets intercepted by MethodValidationInterceptor, which is not the case for test
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.
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
My very basic spring application stopped working and I can't understand what's happened.
pom.xml:
<properties>
<spring.version>4.1.1.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
Config class:
#Configuration
public class MyConfig {
#Bean
public HelloWorld helloWorld() {
return new HelloWorld();
}
}
Bean class:
public class HelloWorld {
private String message;
public void setMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
Entry point of application:
public class MainApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(MyConfig.class);
HelloWorld bean = ctx.getBean(HelloWorld.class);
bean.setMessage("ladjfaj");
System.out.println(bean.getMessage());
}
}
And I'm getting an error
Exception in thread "main" java.lang.IllegalStateException: org.springframework.context.annotation.AnnotationConfigApplicationContext#6ebf8cf5 has not been refreshed yet
at org.springframework.context.support.AbstractApplicationContext.assertBeanFactoryActive(AbstractApplicationContext.java:943)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:967)
at com.nikolas.config.MainApp.main(MainApp.java:12)
You have to call ctx.refresh() before you can call ctx.getBean(HelloWorld.class);
If you dont want to call ctx.refresh() explicitly, just initialize ApplicationContext like this:
new AnnotationConfigApplicationContext(MyConfig.class), then configuration will be registered and refreshed implicitly
Just in case someone has a similar issue and canĀ“t relate directly to the example above, this may help:
I ran into the problem when I had one of my repositories outside of the folder that was included in
#EnableJpaRepositories(basePackages = {"com.myproject.repositores"})
Which lead to this first exception:
Description: Field profileRepository in
com.myproject.featurepackage.config.ProfileService required a bean of
type 'com.myproject.featurepackage.ProfileRepository' that could not
be found. The injection point has the following annotations: -
#org.springframework.beans.factory.annotation.Autowired(required=true)
When I afterwards accessed the ApplicationContext to instanciate a bean, I ran into the error
org.springframework.context.annotation.AnnotationConfigApplicationContext#4983159f
has not been refreshed yet