I have a problem with Arquillian and ManagedExecutorServices. Arquillian is not able to find the default ManagedExecutorService. The Exception is:
Caused by: javax.naming.NameNotFoundException: No object bound to name java:comp/DefaultManagedExecutorService
I am using IntelliJ and execute the test with GlassFish Embedded 3.1 with Arquillian 1.4.0.Final.
Here is my Unit Test:
#Slf4j
#RunWith(Arquillian.class)
public class WorkhorseTest {
#Inject
private Workhorse workhorse;
#Deployment
public static WebArchive createDeployment() {
return ShrinkWrap.create(WebArchive.class)
.addClass(Workhorse.class)
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
}
#Test
public void testExecution() throws Exception {
final Future<Integer> result = workhorse.execute();
result.get(1, TimeUnit.MINUTES);
log.info("Executed...");
}
}
Here is the EJB:
#Slf4j
#Singleton
public class Workhorse {
#Resource
private ManagedExecutorService mes;
public Future<Integer> execute() {
return mes.submit(() -> {
log.info("Hello from a Runnable");
return 5;
});
}
}
How can I test ManagedExecutorServices with Arquillian?
I solved it with switching a dependency in pom.xml:
Old:
<dependency>
<groupId>org.glassfish.main.extras</groupId>
<artifactId>glassfish-embedded-web</artifactId>
<version>5.0</version>
<scope>provided</scope>
</dependency>
New:
<dependency>
<groupId>org.glassfish.main.extras</groupId>
<artifactId>glassfish-embedded-all</artifactId>
<version>5.0</version>
<scope>provided</scope>
</dependency>
Related
I tried to implement the polymorphism in the service layer.
and injecting component was working in the controller.
But It doesn't worked trying to use validation api with #Valid #Validated
#Validated
public interface SearchService<K, V> {
V search(#NotNull #Valid K key);
}
#Service
public class UserSearchService implements SearchService<Email, UserDto> {
private final UserDao userDao;
private final Converter<User, UserDto> converter;
public UserSearchService(UserDao userDao, Converter<User, UserDto> converter) {
this.userDao = userDao;
this.converter = converter;
}
#Override
public UserDto search(Email email) {
try {
User entity = userDao.findByEmail(email.get());
return converter.convert(entity);
} catch (NoResultException noResultException) {
throw new NotExistDataException("user not found", email.get());
}
}
}
#RestController
#Validated
public class UserSearchController {
private final SearchService<Email, UserDto> searchService;
public UserSearchController(SearchService<Email, UserDto> searchService) {
this.searchService = searchService;
}
#GetMapping("api/user")
public UserDto handleSearchingUserByEmail(#RequestParam #Valid Email email) {
return searchService.search(email);
}
}
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userSearchController' defined in file [C:\Users\younggon\Desktop\studylog\target\classes\io\zerogone\controller\api\UserSearchController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'io.zerogone.service.search.SearchService<?, ?>' available: expected single matching bean but found 2: blogSearchService,userSearchService
and spring is matching this service too
#Service
public class BlogSearchService implements SearchService<BlogName, BlogDto> {
private final BlogDao blogDao;
private final Converter<Blog, BlogDto> converter;
public BlogSearchService(BlogDao blogDao, Converter<Blog, BlogDto> converter) {
this.blogDao = blogDao;
this.converter = converter;
}
#Override
public BlogDto search(BlogName blogName) {
try {
Blog entity = blogDao.findByName(blogName.get());
return converter.convert(entity);
} catch (NoResultException noResultException) {
throw new NotExistDataException("blog not found", blogName.get());
}
}
}
Actually, #Qualifier can be used to solve it. but I wonder why. help me :(
update.
I tested autowiring works well without #Qualifier and #Validated
public interface SearchService<K, V> {
V search(K key);
}
#RestController
#Validated
public class UserSearchController {
private final SearchService<Email, UserDto> searchService;
public UserSearchController(SearchService<Email, UserDto> searchService) {
this.searchService = searchService;
}
#GetMapping("api/user")
public UserDto handleSearchingUserByEmail(#RequestParam #Valid Email email) {
return searchService.search(email);
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {WebConfiguration.class, DatabaseConfiguration.class}, loader = AnnotationConfigWebContextLoader.class)
#WebAppConfiguration
public class UserSearchControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void handleSearchingUserByEmail() throws Exception {
mockMvc.perform(get("/api/user")
.param("email", "dudrhs571#gmail.com"))
.andExpect(status().isOk())
.andDo(print());
}
}
result is here.
MockHttpServletRequest:
HTTP Method = GET
Request URI = /api/user
Parameters = {email=[dudrhs571#gmail.com]}
Headers = {}
Handler:
Type = io.zerogone.controller.api.UserSearchController
Method = public io.zerogone.model.dto.UserDto io.zerogone.controller.api.UserSearchController.handleSearchingUserByEmail(io.zerogone.model.Email)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {Content-Type=[application/json;charset=UTF-8]}
Content type = application/json;charset=UTF-8
Body = {"id":1,"name":"zeroGone","nickName":"zeroGone","email":"dudrhs571#gmail.com","imageUrl":"url","blogs":[{"id":1,"name":"studylog","introduce":"web platform for team blog","imageUrl":"/img/blog-default.png","members":null,"invitationKey":null}]}
Forwarded URL = null
Redirected URL = null
Cookies = []
But it deosn't works after adding #Validated to SearchService interface
#Validated
public interface SearchService<K, V> {
V search(#NotNull #Valid K key);
}
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userSearchController' defined in file [C:\Users\younggon\Desktop\studylog\target\classes\io\zerogone\controller\api\UserSearchController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'io.zerogone.service.search.SearchService<?, ?>' available: expected single matching bean but found 2: blogSearchService,userSearchService
I guess Spring is autowiring generic type for UserSearchService, BlogSearchService
Actually it works
but after adding #Validated it doesn't works
and testing directly get UserSearchService.class doesnt't works too.
#Test
public void getUserSearchService() {
Assert.assertEquals(UserSearchService.class, webApplicationContext.getBean(UserSearchService.class).getClass());
}
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'io.zerogone.service.search.UserSearchService' available
I wonder Autowiring for SearchService type is worked by distinguish generic type
And why after adding #Validated it doesn't work?
update2.
I know it works well my code in spring boot app
I hope my spring project works well
Here's my project structure summary
<?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>io.zerogone</groupId>
<artifactId>studylog</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring.version>4.3.30.RELEASE</spring.version>
<apache.commons.version>1.4</apache.commons.version>
<jackson.version>2.12.2</jackson.version>
</properties>
<dependencies>
<!--mvc start-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${apache.commons.version}</version>
</dependency>
<!--mvc end-->
<!--test start-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<!--test end-->
<!--database start-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>${apache.commons.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.29.Final</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<!--database end-->
<!--log start-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!--log end-->
<!--validation start-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.13.Final</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.0</version>
</dependency>
<!--validation start-->
</dependencies>
</project>
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = "io.zerogone")
public class WebConfiguration extends WebMvcConfigurerAdapter {
private static final int TEN_MEGA_BYTE = 10 * 1024 * 1024;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/img/**").addResourceLocations("/img/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
}
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(TEN_MEGA_BYTE);
multipartResolver.setMaxUploadSizePerFile(TEN_MEGA_BYTE);
return multipartResolver;
}
#Bean
public ConversionServiceFactoryBean conversionServiceFactory(Set<Converter<?, ?>> converters) {
ConversionServiceFactoryBean conversionServiceFactory = new ConversionServiceFactoryBean();
conversionServiceFactory.setConverters(converters);
return conversionServiceFactory;
}
#Bean
public ConversionService conversionService(ConversionServiceFactoryBean factory) {
return factory.getObject();
}
#Bean
public ConfigurableWebBindingInitializer webBindingInitializer(ConversionService conversionService) {
ConfigurableWebBindingInitializer configurableWebBindingInitializer = new ConfigurableWebBindingInitializer();
configurableWebBindingInitializer.setConversionService(conversionService);
return configurableWebBindingInitializer;
}
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
public class ApplicationInitializer implements WebApplicationInitializer {
private static final String ROOT_PACKAGE = "io.zerogone";
private static final String DISPATCHER_NAME = "dispatcher";
private static final int DISPATCHER_LOAD_NUMBER = 1;
private static final String DISPATCHER_MAPPING_URL = "/";
#Override
public void onStartup(ServletContext servletContext) {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.scan(ROOT_PACKAGE);
servletContext.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic appServlet = servletContext.addServlet(DISPATCHER_NAME, new DispatcherServlet(new GenericWebApplicationContext()));
appServlet.setLoadOnStartup(DISPATCHER_LOAD_NUMBER);
appServlet.addMapping(DISPATCHER_MAPPING_URL);
}
}
and Tell me necessary information for help
Thanks for any help
It solved!!!
I knew why spring throws exception at autowiring.
This problem is caused by my validation configuration.
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = "io.zerogone")
public class WebConfiguration extends WebMvcConfigurerAdapter {
//etc
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor(); // this is
}
}
MethodValidationPostProcessor creates proxy beans that attached #Validated.
This proxy beans based jdk-based proxy.
So when Spring is autowiring, It may be confused because it lost SearchService implementation information.
Read this.
https://github.com/spring-projects/spring-boot/issues/17000
and my project's Spring boot version works well because it based on CGLib proxy.
This problem solved after MethodValidationPostProcessor
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
postProcessor.setProxyTargetClass(true); // it configures based on CGLib proxy
return postProcessor;
}
Therefore, I can autowiring without #Qualifier for generic implementation.
Things to read further.
What is the difference between JDK dynamic proxy and CGLib?
https://www.baeldung.com/spring-autowire-generics
I found your error very interesting and created the same project that you have. It seems that for me the spring boot app can start without any errors even with #Validated annotation
So something else is going on in your project
I would like to know if it is possible migrate this kind of rule(junit) to vertx-junit5 way.
The original example is the RunOnContextTest.java from the public vertx-example repository in github.
Here is the code:
#RunWith(VertxUnitRunner.class)
public class RunOnContextTest {
/*
* This rule wraps the junit calls in a Vert.x context, the Vert.x instance can be created by the
* rule or provided like in this case.
*/
#Rule
public final RunTestOnContext rule = new RunTestOnContext(Vertx::vertx);
private Thread thread;
#Before
public void before(TestContext context) {
context.assertTrue(Context.isOnEventLoopThread());
thread = Thread.currentThread();
}
#Test
public void theTest(TestContext context) {
context.assertTrue(Context.isOnEventLoopThread());
context.assertEquals(thread, Thread.currentThread());
}
#After
public void after(TestContext context) {
context.assertTrue(Context.isOnEventLoopThread());
context.assertEquals(thread, Thread.currentThread());
}
And the highlighted dependencies are:
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-unit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
Vert.x has added support for this recently.
With Vert.x 4.2.0 you can use:
#RegisterExtension
public final RunTestOnContext rt = new RunTestOnContext();
reference in github -> https://github.com/vert-x3/vertx-junit5/issues/100
I'm feeling stupid to ask this, but I can't understand where I'm wrong with my code.
The context is :
a Spring Boot application (1.5.7) with an embedded Jetty server and a
controller to expose some endpoints
a unique #Configuration class, where some of my beans are defined (Singleton and Prototype scopes)
a #Service that uses some beans defined in my #Configuration class
The problem is:
a NoSuchBeanDefinitionException for one of my #Configuration bean.
Now the details:
My SpringBootApplication :
#SpringBootApplication
public class HbbTVApplication {
public static void main(String[] args) {
SpringApplication.run(HbbTVApplication.class, args);
}
}
My #Configuration class:
#Configuration
#Profile(value = { "dev", "int", "pre", "pro" })
public class StandaloneFrontalConfig extends WebMvcConfigurerAdapter {
#Value("${kafka.bootstrap-servers}")
private String bootstrapServers;
#Bean
public Map<String, Object> producerConfigs() {
Map<String, Object> props = new HashMap<>();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
return props;
}
#Bean
public ProducerFactory<String, String> producerFactory() {
return new DefaultKafkaProducerFactory<>(producerConfigs());
}
#Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/standalone/");
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedHeaders("*");
}
};
}
#Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
#Bean
public Security securityManager() {
return new Security();
}
#Bean
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public KngAflow getTechnicalCookie() {
return new KngAflow();
}
#Bean
public EmbeddedServletContainerCustomizer customizer() {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (container instanceof JettyEmbeddedServletContainerFactory) {
customizeJetty((JettyEmbeddedServletContainerFactory) container);
}
}
private void customizeJetty(JettyEmbeddedServletContainerFactory jetty) {
jetty.addServerCustomizers(new JettyServerCustomizer() {
#Override
public void customize(Server server) {
for (Connector connector : server.getConnectors()) {
if (connector instanceof ServerConnector) {
HttpConnectionFactory connectionFactory = ((ServerConnector) connector)
.getConnectionFactory(HttpConnectionFactory.class);
connectionFactory.getHttpConfiguration().setCookieCompliance(CookieCompliance.RFC2965);
}
}
}
});
}
};
}
}
My #Service:
#Service
public class CookieService implements services.CookieService, InitializingBean {
/**
* Serializable
*/
private static final long serialVersionUID = -1997257884335775587L;
#Autowired
ApplicationContext app;
#Override
public Cookie createTechnicalCookie() {
return new Cookie(app.getBean(KngAflow.class), null);
}
#Override
public void afterPropertiesSet() throws Exception {
if (app != null) {
for (String bean : app.getBeanDefinitionNames()) {
System.out.println("Bean: " + bean);
}
}
}
}
And the "non defined" bean:
#JsonInclude(Include.NON_NULL)
#JsonIgnoreProperties({ "security", "maxAge", "domain", "updated" })
public class KngAflow implements Serializable, InitializingBean {
#JsonProperty(value = "did")
private String did;
#JsonProperty(value = "checksum")
private String checksum;
#Autowired
private Security security;
private Integer maxAge;
private String domain;
private boolean updated = false;
public KngAflow() {
domain = ".mydomain.com";
}
#Override
public void afterPropertiesSet() throws Exception {
did = UUID.randomUUID().toString();
maxAge = 365 * 24 * 60 * 60;
checksum = security.encrypt(did + security.md5(did));
}
}
NB: Classes are not complete, and there are more classes in my project. I only put what I saw as relevant information.
If something else is needed, just ask me please.
By the way, all the endpoints are defined into a unique #Controller class, and all the endpoints are working except those needing the getTechCookie #Bean.
So, my problem occurs in runtime execution. When I start my Spring Boot app, Jetty is started and listening on the configured port.
Nevertheless, if you look at the CookieService #Service, I'm listing all the bean names defined in the autowired context and my getTechnicalCookie (a.k.a KngAflow) #Bean is missing. I can't understand why.
Of course, when I invoke my #controller to execute my #Service code, the NoSuchBeanDefinitionException is thrown executing the line app.getBean(KngAflow.class).
I tried to use a bean name instead of bean type, no change.
For testing purpose (as it doesn't make sense from a logical point of view), I defined my bean getTechCookie #Bean as a Singleton scoped bean, and the name is still missing from the ApplicationContext.
And the last but not least thing is: Everything works fine with Eclipse!
I mean, all my devs are done using Eclipse IDE. My Spring Boot app is built with Maven and executing it inside Eclipse works correctly (and my getTechCookie Bean is defined and listed).
When I package my app using the Maven Spring Boot plugin and execute it using java -jar, my getTechCookie (KngAflow.class) bean is missing. Nevertheless, this class is present inside the jar.
Spring parameters to launch the spring boot app are spring default values (port 8080, no SSL, ...) and the active.profiles are always between dev, int, pre or pro (those defined in my #Configuration class)
What am I doing wrong?
Thanks!
If it helps, I add my POM definition:
<?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>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>com.mydomain.bigdata</groupId>
<artifactId>mybigapp</artifactId>
<version>1.1-SNAPSHOT</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-starter-jetty</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-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*</include>
<include>application.yml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
EDIT: I changed my #Service class to "force" spring to accept my class as a prototype bean, and it works. It's very ugly but it works. But if someone could help me to find what's wrong, I don't like this workaround:
#Override
public void afterPropertiesSet() throws Exception {
if (!context.containsBeanDefinition(KngAflow.class.getName()))
context.registerBeanDefinition(KngAflow.class.getName(),
BeanDefinitionBuilder.genericBeanDefinition(KngAflow.class).setScope("prototype").getBeanDefinition());
}
I made a following simple application to reproduce issue.
#SpringBootApplication
public class Application {
public static void main(String[] args) {
run(Application.class, args);
}
}
#Configuration
#Profile("dev")
public class BeanConfiguration {
#Bean
#Scope(scopeName = SCOPE_PROTOTYPE)
public PrototypeBean prototypeBean() {
return new PrototypeBean();
}
}
public class PrototypeBean {}
#Service
#Slf4j
public class SingletonBean implements InitializingBean {
#Autowired
private ApplicationContext context;
public PrototypeBean getPrototypeBean() {
return context.getBean(PrototypeBean.class);
}
#Override
public void afterPropertiesSet() throws Exception {
for (String name : context.getBeanDefinitionNames()) {
Class<?> c = context.getBean(name).getClass();
log.debug("===> Name: {}, Type = {}", name, c.getTypeName());
}
}
}
#RestController
#RequestMapping("/bean")
public class BeanRestController {
#Autowired
private SingletonBean singletonBean;
#GetMapping("/name")
public String getName() {
return singletonBean.getPrototypeBean().getClass().getName();
}
}
When I execute application with -Dspring.profiles.active=dev setting
Then I see in the log without no issue and REST endpoint gives back response properly:
===> Name: prototypeBean, Type = PrototypeBean
But if I execute application without profile setting
Then I see error in the log and REST endpoint raise exception:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'PrototypeBean' available
I have an Aspect with a pointcut within all RestController annotated controllers:
#Aspect
#Component
public class ControllerLoggingAspect {
Logger log = LoggerFactory.getLogger(this.getClass());
#Pointcut("within(#org.springframework.web.bind.annotation.RestController *)")
public void controller() {
}
#Before("controller()")
public void logBefore(JoinPoint joinPoint) {
log.info("START REST CONTROLLER");
}
#After("controller()")
public void logAfter(JoinPoint joinPoint) {
log.info("END REST CONTROLLER");
}
}
pom dependencies:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.7.3</version>
</dependency>
I get the following error in the log:
NoSuchMethodError: org.springframework.core.annotation.OrderUtils.getOrder
I can verify that I do have the OrderUtils class with the getOrder method in my project.
If I remove the #Before and #After annotated methods, Spring starts up with no errors.
Does anyone have an idea on what I'm missing?
Turns out my the versions between my spring-aop and spring-core dependencies differed. Fixing them to match caused AOP to work as expected.
Make AOP class for after annotation:
#Aspect
#Configuration
public class AfterAopAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
#AfterReturning(value = "execution(* com.alok.*.*(..))", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
logger.info("{} returned with value {}", joinPoint, result);
}
#After(value = "execution(* com.alok.*.*(..))")
public void after(JoinPoint joinPoint) {
logger.info("after execution of {}", joinPoint);
}
}
I have the below FeignClient:
#FeignClient(name="FooMS",fallback=CustomerFeign.CustomerFeignImpl.class)
public interface CustomerFeign {
#RequestMapping(value="/bar/{phoneNo}")
List<Long> getFriends(#PathVariable("phoneNo") Long phoneNo);
class CustomerFeignImpl implements CustomerFeign{
#Override
public List<Long> getFriends(Long phoneNo) {
return new ArrayList<Long>(108);
}
}
}
When the FooMS instance is down, I get a 500 error instead of the fallback being executed. Why is this happening?
adding #Component and feign.hystrix.enabled=true works fine
Tag your CustomerFeignImpl as a #Component or create a #Bean out of it.
This works for me with 2020.0.3:
In application.properties
feign.circuitbreaker.enabled=true
In pom.xml
<spring-cloud.version>2020.0.3</spring-cloud.version>
and
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
Thank you, rostlvan!
I am outlining my implementation below:
I am using Spring Cloud version 2020.0.4 and the following configuration worked for me:
in pom.xml, I have these dependencies:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
Though I'm not sure if we need to have both openfeign and hystrix dependencies. Someone can validate that!
In my application.properties I have feign.circuitbreaker.enabled=true
In my Main Application class, I have
#SpringBootApplication
#EnableFeignClients
public class MySpringBootApplication{
public static void main(String[] args) {
SpringApplication.run(MySpringBootApplication.class, args);
}
}
And finally, my Feign Client, fallback and fallback factory:
UserServiceFeignClient.java
#FeignClient(name = "USER-SERVICE", fallbackFactory = UserServiceFallbackFactory.class)
public interface UserServiceFeignClient {
#GetMapping("/api/users/{userId}")
public ResponseEntity<User> getUser(#PathVariable String userId);
}
UserServiceFeignClientFallback.java
public class UserServiceFeignClientFallback implements UserServiceFeignClient{
#Override
public ResponseEntity<User> getUser(String userId) {
return ResponseEntity.ok().body(new User());
}
}
And, UserServiceFeignClientFallbackFactory.java:
#Component
public class UserServiceFallbackFactory implements FallbackFactory<UserServiceFeignClientFallback>{
#Override
public UserServiceFeignClientFallback create(Throwable cause) {
return new UserServiceFeignClientFallback();
}
}
Was facing the problem myself, until I stumbled upon the answer from #rostlvan