I'm getting an error when I try to start Spring Boot using a Java bean mapper. I'm using Eclipse on Windows, Gradle to build. This is just a learning project I'm using to learn these components.
I'm listening to an ActiveMQ Artemis queue, using the incoming data to call a web service, then saving the order response in a MondoDB. All the components are working with the exception of the mapper converting the api response to a MongoDB entity.
Can anyone see what I'm doing wrong here? It's something with how I'm injecting the OrderMapper, but I'm not sure at this point. This is the Spring output:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.4.5)
2021-04-30 14:16:15.860 INFO 2320 --- [ main] c.b.R.RestClientPocApplication : Starting RestClientPocApplication using Java 11.0.9 on PSPLT-F7VYYY2 with PID 2320 (C:\Users\Bwarrick\Workspaces\Java\RESTClientPOC\bin\main started by Bwarrick in C:\Users\Bwarrick\Workspaces\Java\RESTClientPOC)
2021-04-30 14:16:15.863 INFO 2320 --- [ main] c.b.R.RestClientPocApplication : No active profile set, falling back to default profiles: default
2021-04-30 14:16:16.405 INFO 2320 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data MongoDB repositories in DEFAULT mode.
2021-04-30 14:16:16.567 INFO 2320 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 157 ms. Found 1 MongoDB repository interfaces.
2021-04-30 14:16:16.991 INFO 2320 --- [ main] org.mongodb.driver.cluster : Cluster created with settings {hosts=[192.168.56.102:27017], mode=SINGLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms'}
2021-04-30 14:16:17.064 INFO 2320 --- [68.56.102:27017] org.mongodb.driver.connection : Opened connection [connectionId{localValue:1, serverValue:49}] to 192.168.56.102:27017
2021-04-30 14:16:17.064 INFO 2320 --- [68.56.102:27017] org.mongodb.driver.connection : Opened connection [connectionId{localValue:2, serverValue:50}] to 192.168.56.102:27017
2021-04-30 14:16:17.065 INFO 2320 --- [68.56.102:27017] org.mongodb.driver.cluster : Monitor thread successfully connected to server with description ServerDescription{address=192.168.56.102:27017, type=STANDALONE, state=CONNECTED, ok=true, minWireVersion=0, maxWireVersion=9, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=23420200}
2021-04-30 14:16:17.332 WARN 2320 --- [ main] onfigReactiveWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'artemisConsumer' defined in file [C:\Users\Bwarrick\Workspaces\Java\RESTClientPOC\bin\main\com\benwarrick\RESTClientPOC\jms\ArtemisConsumer.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.benwarrick.RESTClientPOC.service.OrderMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
2021-04-30 14:16:17.355 INFO 2320 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-04-30 14:16:17.370 ERROR 2320 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 1 of constructor in com.benwarrick.RESTClientPOC.jms.ArtemisConsumer required a bean of type 'com.benwarrick.RESTClientPOC.service.OrderMapper' that could not be found.
Action:
Consider defining a bean of type 'com.benwarrick.RESTClientPOC.service.OrderMapper' in your configuration.
Here is my component:
package com.benwarrick.RESTClientPOC.jms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import com.benwarrick.RESTClientPOC.persistance.OrderEntity;
import com.benwarrick.RESTClientPOC.persistance.OrderRepository;
import com.benwarrick.RESTClientPOC.service.CoinBaseClientServiceImpl;
import com.benwarrick.RESTClientPOC.service.OrderMapper;
import com.benwarrick.RESTClientPOC.service.OrderResponse;
import com.benwarrick.RESTClientPOC.service.Price;
import com.benwarrick.RESTClientPOC.service.Prices;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Flux;
#Component
public class ArtemisConsumer {
private final OrderRepository orderRepository;
private final OrderMapper orderMapper;
#Autowired
public ArtemisConsumer(OrderRepository orderRepository, OrderMapper orderMapper) {
this.orderRepository = orderRepository;
this.orderMapper = orderMapper;
}
#JmsListener(destination = "test.topic::test.queue")
public void receive(String msg){
System.out.println("Got Message: " + msg);
CoinBaseClientServiceImpl client = new CoinBaseClientServiceImpl();
Mono<OrderResponse>orderCreate = client.createOrder("market", "USD", "BTC", "5");
orderCreate.log().subscribe(
successValue -> processResponse(successValue),
error -> System.err.println(error.getMessage()),
() -> System.out.println("mono consumed")
);
}
public void processResponse(OrderResponse orderResponse) {
System.out.println(orderResponse.getOrderID() + " " + orderResponse.getSellingCurrency() + orderResponse.getBuyingCurrency() + " Qty: "
+ orderResponse.getBoughtQty() + " Type: " + orderResponse.getOrderType()) ;
try {
OrderEntity entity = orderMapper.apiResponseToEntity(orderResponse);
OrderEntity newEntity = orderRepository.save(entity);
System.out.println("Test: " + newEntity.getBoughtQuantity());
}
catch(Exception e) {
System.out.println("Exception: " + e.toString()) ;
}
}
}
Here is my main process:
package com.benwarrick.RESTClientPOC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableAsync;
#SpringBootApplication
#EnableAsync
public class RestClientPocApplication {
private static final Logger LOG = LoggerFactory.getLogger(RestClientPocApplication.class);
public static void main(String[] args) {
ConfigurableApplicationContext ctx =
SpringApplication.run(RestClientPocApplication.class, args);
String mongoDBHost = ctx.getEnvironment().getProperty("spring.data.mongodb.host");
String mongoDbPort = ctx.getEnvironment().getProperty("spring.data.mongodb.port");
LOG.info("Connected to MongoDb: " + mongoDBHost + ":" + mongoDbPort);
}
}
And here is the bean that isn't being found:
package com.benwarrick.RESTClientPOC.service;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import com.benwarrick.RESTClientPOC.persistance.OrderEntity;
#Mapper (componentModel = "spring")
public interface OrderMapper {
#Mappings({
#Mapping(target = "id", ignore = true),
#Mapping(target = "version", ignore = true),
#Mapping(target = "orderId", source="orderID"),
#Mapping(target = "orderID", source="boughtQty")
})
OrderEntity apiResponseToEntity(OrderResponse api);
}
And my build.graddle
plugins {
id 'org.springframework.boot' version '2.4.5'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.benwarrick'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
ext {
mapstructVersion = "1.4.2.Final"
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-artemis'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
implementation("org.mapstruct:mapstruct:${mapstructVersion}")
compileOnly "org.mapstruct:mapstruct-processor:${mapstructVersion}"
annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
testAnnotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
implementation('org.springframework.boot:spring-boot-starter-data-mongodb')
testImplementation('de.flapdoodle.embed:de.flapdoodle.embed.mongo')
}
test {
useJUnitPlatform()
}
For MapStruct you need to correctly set up annotation processor so that IDE (Eclipse in your case) and build tool, Gradle that is play well together.
You can read on MapStruct page uder IDE support about it. Now I didn't manage to get it working without issues that way.
Plugin to the rescue! Now what I recommend you is that you use following Gradle plugin that can set up Eclipse IDE in a proper way for you. The plugin: https://plugins.gradle.org/plugin/com.diffplug.eclipse.apt
I see you know already how to include the plugin, just in case the code that you have to add to build.gradle:
plugins {
...
id 'com.diffplug.eclipse.apt' version "3.29.1"
}
And then run the command from your project that will set up Eclipse:
./gradlew eclipseJdtApt eclipseFactorypath eclipseJdt
From within Eclipse, you now have to run right-click the project and select Gradle / Refresh Gradle Project.
Afterwards, Project / Clean. With this clean build, the annotation-processor should be running.
I hope Eclipse Buildship will pick this up and make it easier to support his feature.
Related
I have recently started exploring Spring boot. I am following https://www.bezkoder.com/spring-boot-jdbctemplate-postgresql-example/ documentation.
I have created all files as instructed in the documentation.
Here goes my code:
******AppApplication.java
`package com.triveni.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication
public class AppApplication {
public static void main(String[] args) {
SpringApplication.run(AppApplication.class, args);
}
}`
***** IProductRepository *****
`package com.triveni.repository;
import com.triveni.models.ProductModel;
import java.util.List;
public interface IProductRepository {
int create(ProductModel productModel);
int update (ProductModel productModel);
ProductModel findById(int id);
List<ProductModel> findAll();
List<ProductModel> findByActive(boolean active);
}`
***** ProductModel.java *****
package com.triveni.models;
public class ProductModel {
private int productId;
private String productName;
private int cost;
private Boolean active;
private int descriptionId;
public ProductModel(int productId, String productName, int cost, Boolean active, int descriptionId){
this.productId = productId;
this.productName = productName;
this.cost = cost;
this.active = active;
this.descriptionId = descriptionId;
}
public void setProductId(int productId){
this.productId = productId;
}
public long getProductId(){
return productId;
}
public void setProductName(String productName){
this.productName = productName;
}
public String getProductName(){
return productName;
}
public void setCost(int cost) {
this.cost = cost;
}
public int getCost() {
return cost;
}
public void setActive(Boolean active) {
this.active = active;
}
public Boolean getActive() {
return active;
}
public void setDescriptionId(int descriptionId) {
this.descriptionId = descriptionId;
}
public int getDescriptionId() {
return descriptionId;
}
}
*** Product Repository *****
package com.triveni.data;
import com.triveni.models.ProductModel;
import com.triveni.repository.IProductRepository;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
public class ProductRepository implements IProductRepository {
#Autowired
private JdbcTemplate jdbcTemplate;
#Override
public int create(ProductModel productModel) {
return jdbcTemplate.update("INSERT INTO public.product(\n" +
"\t\"productId\", \"productName\", cost, \"DescriptionId\", active)\n" +
"\tVALUES (?, ?, ?, ?, ?);",new Object[]{productModel.getProductId(),productModel.getProductName(),
productModel.getCost(), productModel.getDescriptionId(),productModel.getActive()});
}
#Override
public int update(ProductModel productModel) {
return 0;
}
#Override
public ProductModel findById(int id) {
return null;
}
#Override
public List<ProductModel> findAll() {
return jdbcTemplate.query("SELECT \"productId\", \"productName\", cost, \"DescriptionId\", active\n" +
"\tFROM public.product",BeanPropertyRowMapper.newInstance(ProductModel.class));
}
#Override
public List<ProductModel> findByActive(boolean active) {
return null;
}
}
***** ProductController.java *****
package com.triveni.controllers;
import java.util.ArrayList;
import java.util.List;
import com.triveni.data.ProductRepository;
import com.triveni.models.ProductModel;
import com.triveni.repository.IProductRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/api/v1")
public class ProductController {
#Autowired
ProductRepository productRepository;
#GetMapping("/product")
public ResponseEntity<List<ProductModel>> getAllProducts(){
try{
List<ProductModel> products = new ArrayList<ProductModel>();
productRepository.findAll().forEach(products::add);;
return new ResponseEntity<>(products, HttpStatus.OK);
}catch (Exception e){
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
My Project Folder Structure
I am getting following error
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Tue Dec 27 13:51:39 IST 2022
There was an unexpected error (type=Not Found, status=404).
Any help will be highly appreciated.
*Output
/Users/chhayatiwari/Library/Java/JavaVirtualMachines/openjdk-19.0.1/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA CE.app/Contents/lib/idea_rt.jar=51857:/Applications/IntelliJ IDEA CE.app/Contents/bin -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -classpath /Users/chhayatiwari/Desktop/Work/TriveniApp-Service/app/target/classes:/Users/chhayatiwari/.m2/repository/org/springframework/boot/spring-boot-starter-web/3.0.1/spring-boot-starter-web-3.0.1.jar:/Users/chhayatiwari/.m2/repository/org/springframework/boot/spring-boot-starter/3.0.1/spring-boot-starter-3.0.1.jar:/Users/chhayatiwari/.m2/repository/org/springframework/boot/spring-boot/3.0.1/spring-boot-3.0.1.jar:/Users/chhayatiwari/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/3.0.1/spring-boot-autoconfigure-3.0.1.jar:/Users/chhayatiwari/.m2/repository/org/springframework/boot/spring-boot-starter-logging/3.0.1/spring-boot-starter-logging-3.0.1.jar:/Users/chhayatiwari/.m2/repository/ch/qos/logback/logback-classic/1.4.5/logback-classic-1.4.5.jar:/Users/chhayatiwari/.m2/repository/ch/qos/logback/logback-core/1.4.5/logback-core-1.4.5.jar:/Users/chhayatiwari/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.19.0/log4j-to-slf4j-2.19.0.jar:/Users/chhayatiwari/.m2/repository/org/apache/logging/log4j/log4j-api/2.19.0/log4j-api-2.19.0.jar:/Users/chhayatiwari/.m2/repository/org/slf4j/jul-to-slf4j/2.0.6/jul-to-slf4j-2.0.6.jar:/Users/chhayatiwari/.m2/repository/jakarta/annotation/jakarta.annotation-api/2.1.1/jakarta.annotation-api-2.1.1.jar:/Users/chhayatiwari/.m2/repository/org/yaml/snakeyaml/1.33/snakeyaml-1.33.jar:/Users/chhayatiwari/.m2/repository/org/springframework/boot/spring-boot-starter-json/3.0.1/spring-boot-starter-json-3.0.1.jar:/Users/chhayatiwari/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.14.1/jackson-databind-2.14.1.jar:/Users/chhayatiwari/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.14.1/jackson-annotations-2.14.1.jar:/Users/chhayatiwari/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.14.1/jackson-core-2.14.1.jar:/Users/chhayatiwari/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.14.1/jackson-datatype-jdk8-2.14.1.jar:/Users/chhayatiwari/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.14.1/jackson-datatype-jsr310-2.14.1.jar:/Users/chhayatiwari/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.14.1/jackson-module-parameter-names-2.14.1.jar:/Users/chhayatiwari/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/3.0.1/spring-boot-starter-tomcat-3.0.1.jar:/Users/chhayatiwari/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/10.1.4/tomcat-embed-core-10.1.4.jar:/Users/chhayatiwari/.m2/repository/org/apache/tomcat/embed/tomcat-embed-el/10.1.4/tomcat-embed-el-10.1.4.jar:/Users/chhayatiwari/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/10.1.4/tomcat-embed-websocket-10.1.4.jar:/Users/chhayatiwari/.m2/repository/org/springframework/spring-web/6.0.3/spring-web-6.0.3.jar:/Users/chhayatiwari/.m2/repository/io/micrometer/micrometer-observation/1.10.2/micrometer-observation-1.10.2.jar:/Users/chhayatiwari/.m2/repository/io/micrometer/micrometer-commons/1.10.2/micrometer-commons-1.10.2.jar:/Users/chhayatiwari/.m2/repository/org/springframework/spring-webmvc/6.0.3/spring-webmvc-6.0.3.jar:/Users/chhayatiwari/.m2/repository/org/springframework/spring-aop/6.0.3/spring-aop-6.0.3.jar:/Users/chhayatiwari/.m2/repository/org/springframework/spring-context/6.0.3/spring-context-6.0.3.jar:/Users/chhayatiwari/.m2/repository/org/springframework/spring-expression/6.0.3/spring-expression-6.0.3.jar:/Users/chhayatiwari/.m2/repository/org/slf4j/slf4j-api/2.0.6/slf4j-api-2.0.6.jar:/Users/chhayatiwari/.m2/repository/org/springframework/spring-core/6.0.3/spring-core-6.0.3.jar:/Users/chhayatiwari/.m2/repository/org/springframework/spring-jcl/6.0.3/spring-jcl-6.0.3.jar:/Users/chhayatiwari/.m2/repository/org/springframework/boot/spring-boot-starter-data-jdbc/3.0.1/spring-boot-starter-data-jdbc-3.0.1.jar:/Users/chhayatiwari/.m2/repository/org/springframework/boot/spring-boot-starter-jdbc/3.0.1/spring-boot-starter-jdbc-3.0.1.jar:/Users/chhayatiwari/.m2/repository/com/zaxxer/HikariCP/5.0.1/HikariCP-5.0.1.jar:/Users/chhayatiwari/.m2/repository/org/springframework/data/spring-data-jdbc/3.0.0/spring-data-jdbc-3.0.0.jar:/Users/chhayatiwari/.m2/repository/org/springframework/data/spring-data-relational/3.0.0/spring-data-relational-3.0.0.jar:/Users/chhayatiwari/.m2/repository/org/springframework/data/spring-data-commons/3.0.0/spring-data-commons-3.0.0.jar:/Users/chhayatiwari/.m2/repository/org/postgresql/postgresql/42.5.1/postgresql-42.5.1.jar:/Users/chhayatiwari/.m2/repository/org/checkerframework/checker-qual/3.5.0/checker-qual-3.5.0.jar:/Users/chhayatiwari/.m2/repository/org/springframework/spring-jdbc/5.3.22/spring-jdbc-5.3.22.jar:/Users/chhayatiwari/.m2/repository/org/springframework/spring-beans/6.0.3/spring-beans-6.0.3.jar:/Users/chhayatiwari/.m2/repository/org/springframework/spring-tx/6.0.3/spring-tx-6.0.3.jar com.triveni.app.AppApplication
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.0.1)
2022-12-27T20:17:11.351+05:30 INFO 3695 --- [ main] com.triveni.app.AppApplication : Starting AppApplication using Java 19.0.1 with PID 3695 (/Users/chhayatiwari/Desktop/Work/TriveniApp-Service/app/target/classes started by chhayatiwari in /Users/chhayatiwari/Desktop/Work/TriveniApp-Service/app)
2022-12-27T20:17:11.353+05:30 INFO 3695 --- [ main] com.triveni.app.AppApplication : No active profile set, falling back to 1 default profile: "default"
2022-12-27T20:17:11.664+05:30 INFO 3695 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JDBC repositories in DEFAULT mode.
2022-12-27T20:17:11.671+05:30 INFO 3695 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 4 ms. Found 0 JDBC repository interfaces.
2022-12-27T20:17:11.919+05:30 INFO 3695 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2022-12-27T20:17:11.924+05:30 INFO 3695 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2022-12-27T20:17:11.925+05:30 INFO 3695 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.4]
2022-12-27T20:17:11.972+05:30 INFO 3695 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2022-12-27T20:17:11.973+05:30 INFO 3695 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 589 ms
2022-12-27T20:17:12.162+05:30 INFO 3695 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2022-12-27T20:17:12.282+05:30 INFO 3695 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection#6680f714
2022-12-27T20:17:12.283+05:30 INFO 3695 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2022-12-27T20:17:12.343+05:30 INFO 3695 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2022-12-27T20:17:12.348+05:30 INFO 3695 --- [ main] com.triveni.app.AppApplication : Started AppApplication in 1.206 seconds (process running for 1.435)
2022-12-27T20:19:18.599+05:30 INFO 3695 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-12-27T20:19:18.599+05:30 INFO 3695 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2022-12-27T20:19:18.604+05:30 INFO 3695 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms
try to add this to annotation #CrossOrigin("*") after #RestController.
I believe you are just launching the spring application and on launch, this error shows up. In order to test your URL you can type
<YOUR-URL>/app/v1/product
This should return you the Response you are looking for.
Remove ResponseEntity<List> from method and instead return a simple String and check if the method invoked or not. If it works properly then go to the next step and invoke the repository.
My multi-tenant application needs to periodically check a set of SFTP folders for an ingestion file. The SFTP folders and parameters are defined in my application.properties file, and the number is dynamic.
ingestion.carriers=carrier1,carrier2
ingestion.carrier1=sftp
ingestion.carrier1.sftp.host=localhost
ingestion.carrier1.sftp.username=sftp
ingestion.carrier1.sftp.password=sftp
ingestion.carrier1.sftp.remotedir=carrier1
ingestion.carrier1.sftp.localdir=sftp/carrier1
ingestion.carrier1.sftp.archivedir=sftp/carrier1/archive
ingestion.carrier1.sftp.errordir=sftp/carrier1/error
ingestion.carrier1.ping=7000
ingestion.carrier2=sftp
ingestion.carrier2.sftp.host=localhost
ingestion.carrier2.sftp.username=sftp
ingestion.carrier2.sftp.password=sftp
ingestion.carrier2.sftp.remotedir=carrier2
ingestion.carrier2.sftp.localdir=sftp/carrier2
ingestion.carrier2.sftp.archivedir=sftp/carrier2/archive
ingestion.carrier2.sftp.errordir=sftp/carrier2/error
ingestion.carrier2.pingFrequency=13000
I need to dinamically create all the necessary beans to enable spring integration flow. To do so, I've tried to set up a BeanFactoryPostProcessor, as I cannot declare the beans in a "static" way. This processor is thought to be configured, in future, with different methods to retrieve the file: because of this, the actual creation of beans is delegated to another class.
This is the post processor...
package mypkg.batch.config.integration;
import mypkg.batch.config.integration.factory.SFTPBeansFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.util.List;
#Component
public class IngestionBeanConfigurator implements BeanFactoryPostProcessor {
public static final Logger LOG = LoggerFactory.getLogger(IngestionBeanConfigurator.class);
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory fact) throws BeansException {
Environment env = fact.getBean(Environment.class);
String carriersList = env.getProperty("ingestion.carriers");
if (carriersList == null) {
LOG.info("No ingestion has been defined");
return;
}
List<String> carriers = List.of(carriersList.split(","));
for (String carrier : carriers) {
String carrierMethod = env.getProperty("ingestion.%s".formatted(carrier));
if (carrierMethod != null) {
if ("sftp".equals(carrierMethod)) {
new SFTPBeansFactory(carrier, env).loadBeans(fact);
} else {
LOG.warn("Invalid carrier method {} for carrier {}", carrierMethod, carrier);
}
}
}
}
}
... and this is the class creating SFTP beans
package com.eyemed.foodogs.batch.config.integration.factory;
import com.eyemed.foodogs.model.exception.MembersMessageHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.MessageChannelSpec;
import org.springframework.integration.dsl.Pollers;
import org.springframework.integration.dsl.StandardIntegrationFlow;
import org.springframework.integration.dsl.context.IntegrationFlowContext;
import org.springframework.integration.file.filters.AcceptOnceFileListFilter;
import org.springframework.integration.sftp.filters.SftpSimplePatternFileListFilter;
import org.springframework.integration.sftp.inbound.SftpInboundFileSynchronizer;
import org.springframework.integration.sftp.inbound.SftpInboundFileSynchronizingMessageSource;
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
import java.io.File;
import java.math.BigInteger;
import java.util.concurrent.TimeUnit;
public class SFTPBeansFactory implements BeanFactory {
public static final Logger LOG = LoggerFactory.getLogger(SFTPBeansFactory.class);
private final String carrierId;
private final String sftpHost;
private final String sftpUsername;
private final String sftpPassword;
private final String sftpRemoteDir;
private final String sftpLocalDir;
private final String sftpArchiveDir;
private final String sftpErrorDir;
private final BigInteger pingFrequency;
public SFTPBeansFactory(final String carrierId, final Environment props) {
String prefix = "ingestion.%s".formatted(carrierId);
this.carrierId = carrierId;
this.sftpHost = props.getProperty("%s.sftp.host".formatted(prefix));
this.sftpUsername = props.getProperty("%s.sftp.username".formatted(prefix));
this.sftpPassword = props.getProperty("%s.sftp.password".formatted(prefix));
this.sftpRemoteDir = props.getProperty("%s.sftp.remotedir".formatted(prefix));
this.sftpLocalDir = props.getProperty("%s.sftp.localdir".formatted(prefix));
this.sftpArchiveDir = props.getProperty("%s.sftp.archivedir".formatted(prefix));
this.sftpErrorDir = props.getProperty("%s.sftp.errordir".formatted(prefix));
String pingFrequencyString = props.getProperty("%s.ping".formatted(prefix));
if (pingFrequencyString != null) {
this.pingFrequency = new BigInteger(pingFrequencyString);
} else {
this.pingFrequency = BigInteger.valueOf(3600000);
}
}
public void loadBeans(ConfigurableBeanFactory fact) {
DefaultSftpSessionFactory sf = _buildSessionFactory();
SftpInboundFileSynchronizer sync = _buildInboundFileSynchronizer(sf);
fact.registerSingleton("sftp-sync-%s".formatted(carrierId), sync);
SftpInboundFileSynchronizingMessageSource src = _buildMessageSource(sync);
MembersMessageHandler handler = new MembersMessageHandler(carrierId, fact.getBean(JobLauncher.class), fact.getBean("readMembersJob", Job.class));
String beanName = "sftp-flow-%s".formatted(carrierId);
String channelName = "sftp-ingestion-channel-%s".formatted(carrierId);
LOG.info("Creating bean %s based on channel %s".formatted(beanName, channelName));
StandardIntegrationFlow flow = IntegrationFlows
.from(src, c -> c.poller(Pollers.fixedRate(pingFrequency.longValue(), TimeUnit.MILLISECONDS, 0)))
.channel(channelName)
.handle(handler)
.get();
IntegrationFlowContext ctx = fact.getBean(IntegrationFlowContext.class);
ctx.registration(flow).id(beanName).autoStartup(true).register();
flow.start();
}
private SftpInboundFileSynchronizingMessageSource _buildMessageSource(SftpInboundFileSynchronizer sync) {
var src = new SftpInboundFileSynchronizingMessageSource(sync);
src.setLocalDirectory(new File(sftpLocalDir));
src.setAutoCreateLocalDirectory(true);
src.setLocalFilter(new AcceptOnceFileListFilter<>());
return src;
}
private SftpInboundFileSynchronizer _buildInboundFileSynchronizer(DefaultSftpSessionFactory sf) {
var sync = new SftpInboundFileSynchronizer(sf);
sync.setDeleteRemoteFiles(true);
sync.setRemoteDirectory(sftpRemoteDir);
sync.setFilter(new SftpSimplePatternFileListFilter("*.csv"));
sync.setLocalFilenameGeneratorExpressionString(
"#this.substring(0, #this.length - 4) + '_%s_' + new com.eyemed.foodogs.application.util.TimestampProvider().currentTimestamp() + '.txt'".formatted(carrierId));
return sync;
}
private DefaultSftpSessionFactory _buildSessionFactory() {
var sf = new DefaultSftpSessionFactory();
sf.setHost(sftpHost);
sf.setUser(sftpUsername);
sf.setPassword(sftpPassword);
sf.setPort(22);
sf.setAllowUnknownKeys(true);
return sf;
}
}
Unfortunately, this doesn't seem to work: SFTP files are not read and remain sadly in the source folder. The local SFTP works, as the previous version with the "static" beans used to work correctly.
Also, I do not see errors in the log
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.5)
2022-10-18 11:42:15.396 INFO 8226 --- [ main] com.eyemed.foodogs.application.App : Starting App using Java 17 on A-M-L-MUTI.local with PID 8226 (/Users/lorenzomuti/Repositories/FooDogs/backend/foodogsbootapplication/target/classes started by lorenzomuti in /Users/lorenzomuti/Repositories/FooDogs/backend/foodogsbootapplication)
2022-10-18 11:42:15.399 INFO 8226 --- [ main] com.eyemed.foodogs.application.App : The following profiles are active: dev
2022-10-18 11:42:17.988 INFO 8226 --- [ main] c.e.f.b.c.i.factory.SFTPBeansFactory : Creating bean sftp-flow-carrier1 based on channel sftp-ingestion-channel-carrier1
2022-10-18 11:42:18.028 INFO 8226 --- [ main] c.e.f.b.c.i.factory.SFTPBeansFactory : Creating bean sftp-flow-carrier2 based on channel sftp-ingestion-channel-carrier2
2022-10-18 11:42:18.038 INFO 8226 --- [ main] faultConfiguringBeanFactoryPostProcessor : No bean named 'errorChannel' has been explicitly defined. Therefore, a default PublishSubscribeChannel will be created.
2022-10-18 11:42:18.049 INFO 8226 --- [ main] faultConfiguringBeanFactoryPostProcessor : No bean named 'integrationHeaderChannelRegistry' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created.
2022-10-18 11:42:18.311 INFO 8226 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.integration.config.IntegrationManagementConfiguration' of type [org.springframework.integration.config.IntegrationManagementConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2022-10-18 11:42:18.322 INFO 8226 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'integrationChannelResolver' of type [org.springframework.integration.support.channel.BeanFactoryChannelResolver] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2022-10-18 11:42:18.324 INFO 8226 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'integrationDisposableAutoCreatedBeans' of type [org.springframework.integration.config.annotation.Disposables] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2022-10-18 11:42:18.659 INFO 8226 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2022-10-18 11:42:18.675 INFO 8226 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2022-10-18 11:42:18.676 INFO 8226 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.53]
2022-10-18 11:42:18.816 INFO 8226 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2022-10-18 11:42:18.816 INFO 8226 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 3337 ms
2022-10-18 11:42:18.892 DEBUG 8226 --- [ main] o.s.w.f.CommonsRequestLoggingFilter : Filter 'logFilter' configured for use
2022-10-18 11:42:19.474 INFO 8226 --- [ main] c.e.f.application.config.UnionPayConfig : Activating UnionPay Service logger
2022-10-18 11:42:20.478 INFO 8226 --- [ main] o.s.b.c.r.s.JobRepositoryFactoryBean : No database type set, using meta data indicating: MYSQL
2022-10-18 11:42:20.501 INFO 8226 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : No TaskExecutor has been set, defaulting to synchronous executor.
2022-10-18 11:42:20.730 INFO 8226 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#7a0f06ad, org.springframework.security.web.context.SecurityContextPersistenceFilter#3e9fb485, org.springframework.security.web.header.HeaderWriterFilter#580ffea, org.springframework.security.web.authentication.logout.LogoutFilter#7fe87c0e, org.springframework.security.web.authentication.www.BasicAuthenticationFilter#c82d925, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#38dbeb39, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#48106381, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#1fa9692b, org.springframework.security.web.session.SessionManagementFilter#2ffb0d10, org.springframework.security.web.access.ExceptionTranslationFilter#f76872f, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#6df2a206]
2022-10-18 11:42:20.849 INFO 8226 --- [ main] o.s.i.endpoint.EventDrivenConsumer : Adding {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel
2022-10-18 11:42:20.849 INFO 8226 --- [ main] o.s.i.channel.PublishSubscribeChannel : Channel 'application.errorChannel' has 1 subscriber(s).
2022-10-18 11:42:20.849 INFO 8226 --- [ main] o.s.i.endpoint.EventDrivenConsumer : started bean '_org.springframework.integration.errorLogger'
2022-10-18 11:42:20.867 INFO 8226 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2022-10-18 11:42:20.887 INFO 8226 --- [ main] com.eyemed.foodogs.application.App : Started App in 6.399 seconds (JVM running for 7.221)
Is my approach correct? Should I do something more? Please help me, as I do not know where to bump my head :)
Consider to move you logic into some #PostConstruct method instead.
I think getting access to bean factory and starting beans from the BeanFactoryPostProcessor is too early.
Also consider to use a SftpInboundChannelAdapterSpec instead of manual creation. And don't register those beans manually - rely on the IntegrationFlowContext.
I wanted to suggest you to look into this also: https://docs.spring.io/spring-integration/docs/current/reference/html/sftp.html#sftp-rotating-server-advice.
But looks like you use that carrierId in the message handler. Although it may come as a message header. Not sure also if you really need that .channel(channelName) in between.
I am trying to add a prefix or suffix in the controller at the method level on top of GET, POST, PUT, DELETE mappings.
Controller class
#RestController
#RequestMapping("/something")
public class SomeController {
#PutMapping("/some/path/")
public ResponseEntity<ApiResponse<String>> someMethod() {
....
}
...
}
So, basically, the above request URL should be something like : http://localhost:8080/something/some/path/
Now, I just want to add some prefix or suffix whatever is feasible to the request URL which will be something like : http://localhost:8080/something/read/some/path/ or http://localhost:8080/something/some/path/read/ the extra "/read" which needs to be added to the request URL as a prefix or suffix. I can do this directly by adding this to the PutMapping value, but I want to decorate it somewhat using annotation like #Read
So, the updated Controller class will be like
#RestController
#RequestMapping("/something")
public class SomeController {
#Read
#PutMapping("/some/path/")
public ResponseEntity<ApiResponse<String>> someMethod() {
....
}
...
}
and the same way updated request URL will be like : http://localhost:8080/something/read/some/path/
I am unable to find a better way to do this. Till now I have only achieved adding a class-level prefix using custom annotation.
Can anyone please help with the above requirement.?
Thank you !!
I am also curious to know whether is achievable or not even?
Using such way of path extension make your code less understandable. (maybe you should read more about RESTful API) But spring can do almost everything you want.
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.stream.Stream;
#SpringBootApplication
public class DemoApplication {
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Read {
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Write {
}
#Bean
public WebMvcRegistrations webMvcRegistrations() {
return new WebMvcRegistrations() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping() {
#Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo defaultRequestMappingInfo = super.getMappingForMethod(method, handlerType);
if (defaultRequestMappingInfo == null) {
return null;
}
String pathSuffix;
if (method.isAnnotationPresent(Read.class)) {
pathSuffix = "read";
} else if (method.isAnnotationPresent(Write.class)) {
pathSuffix = "write";
} else {
return defaultRequestMappingInfo;
}
//extend path by mutating configured request mapping info
RequestMappingInfo.Builder mutateBuilder = defaultRequestMappingInfo.mutate();
mutateBuilder.paths(
defaultRequestMappingInfo.getPatternValues().stream()
.map(path -> path + "/" + pathSuffix)
.toArray(String[]::new)
);
return mutateBuilder.build();
}
};
}
};
}
#RestController
#RequestMapping("/books")
public static class BooksController {
#Read
#GetMapping("/{id}")
public String readBook(#PathVariable("id") String bookId) {
return bookId;
}
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Extension point is here, you can change path like you want.
Example:
request: http://localhost:8080/books/asd
response: 404
output:
2022-06-27 10:49:48.671 DEBUG 8300 --- [nio-8080-exec-2] com.example.demo.DemoApplication$1$1 : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#errorHtml(HttpServletRequest, HttpServletResponse)
request: http://localhost:8080/books/asd/read
response: asd
output:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.0)
2022-06-27 10:48:53.622 INFO 8300 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication using Java 1.8.0_312 on DESKTOP with PID 8300 ()
2022-06-27 10:48:53.624 DEBUG 8300 --- [ main] com.example.demo.DemoApplication : Running with Spring Boot v2.7.0, Spring v5.3.20
2022-06-27 10:48:53.625 INFO 8300 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to 1 default profile: "default"
2022-06-27 10:48:54.227 INFO 8300 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2022-06-27 10:48:54.233 INFO 8300 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2022-06-27 10:48:54.233 INFO 8300 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.63]
2022-06-27 10:48:54.298 INFO 8300 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2022-06-27 10:48:54.298 INFO 8300 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 643 ms
2022-06-27 10:48:54.473 DEBUG 8300 --- [ main] com.example.demo.DemoApplication$1$1 : 3 mappings in 'requestMappingHandlerMapping'
2022-06-27 10:48:54.536 INFO 8300 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2022-06-27 10:48:54.543 INFO 8300 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 1.199 seconds (JVM running for 1.827)
2022-06-27 10:49:01.196 INFO 8300 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-06-27 10:49:01.196 INFO 8300 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2022-06-27 10:49:01.197 INFO 8300 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
2022-06-27 10:49:01.210 DEBUG 8300 --- [nio-8080-exec-1] com.example.demo.DemoApplication$1$1 : Mapped to com.example.demo.DemoApplication$BooksController#readBook(String)
dependencies
org.springframework.boot:spring-boot-starter-web
application.properties
logging.level.com.example.demo=debug
Today I encountered a NullPointExcepiton When I tried to use an AOP Logger in java to print logs of a class which contains a BiPredicate(FuncionalInterface).
Problem:
Is there any wrong usage of FunctionalInterface with AOP?
When I set the pointCut to BiPredicateExample.java, the exception will
be raised by biPredicateExample.cmp(FuntionalInterface), while the
method in this class works fun(biPredicateExample.cmp1()).
The NullPointExcepiton can be reproduced using following codes:
Github repository link here
com/test/BiPredicateExample.java
import org.springframework.stereotype.Component;
import java.util.function.BiPredicate;
#Component
public class BiPredicateExample {
public BiPredicate<Integer,Integer> cmp = (x,y) -> (x>y);
public boolean cmp1(Integer x, Integer y){
return x>y;
}
}
com/logger/BiPredicateExample.java
package com.logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class LogAspect {
/**
* BiPredicateExample works fine.
*/
// #Pointcut("execution(* com.empty.*.*(..) )")
/**
* It'll raise NullPointException When this PointCut cut
* into the FunctionalInterface.
*/
#Pointcut("execution(* com.test.*.*(..) )")
public void logPointCut(){}
#Before("logPointCut()")
public void printBeforeMethodInvoke(JoinPoint joinPoint){
}
}
com/Application.java
package com;
import com.test.BiPredicateExample;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application implements CommandLineRunner{
#Autowired
BiPredicateExample biPredicateExample;
public static void main(String[]args){
SpringApplication.run(Application.class,args);
}
#Override
public void run(String... strings) throws Exception {
boolean w=biPredicateExample.cmp1(10,2);
System.out.println(w);
boolean f=biPredicateExample.cmp.test(10,2);
System.out.println(f);
}
}
Thanks!
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>groupId</groupId>
<artifactId>AOP_FunctionalInterface_Test</artifactId>
<version>1.0-SNAPSHOT</version>
<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-aop</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
</dependencies>>
</project>
The problem is that the member variable cmp only has a meaningful value in the original class, but as soon as you use Spring AOP, you are actually interacting with a dynamic proxy object. JDK dynamic proxies, however, only proxy public method calls (CGLIB ones also protected and package-scoped methods). There is no proxying mechanism for member variables, you need to make sure that you do not directly access internal state via member access from outside but e.g. via getter method.
I.e. in this case you change biPredicateExample.cmp into biPredicateExample.getCmp(), that's all:
package com.test;
import org.springframework.stereotype.Component;
import java.util.function.BiPredicate;
#Component
public class BiPredicateExample {
private BiPredicate<Integer, Integer> cmp = (x, y) -> (x > y);
public boolean cmp1(Integer x, Integer y) {
return x > y;
}
public BiPredicate<Integer, Integer> getCmp() {
return cmp;
}
}
package com;
import com.test.BiPredicateExample;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application implements CommandLineRunner {
#Autowired
BiPredicateExample biPredicateExample;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(String... strings) throws Exception {
System.out.println(biPredicateExample.cmp1(10, 2));
System.out.println(biPredicateExample.getCmp().test(10, 2));
}
}
package com.logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class LogAspect {
#Pointcut("execution(* com.test..*(..) )")
public void logPointCut() {}
#Before("logPointCut()")
public void printBeforeMethodInvoke(JoinPoint joinPoint) {
System.out.println(joinPoint);
}
}
Then the console log changes into:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.3.RELEASE)
2018-06-16 14:15:10.040 INFO 12456 --- [ main] com.Application : Starting Application on Xander-Ultrabook with PID 12456 (C:\Users\alexa\Documents\java-src\AOP_FunctionalInterface_Test\target\classes started by alexa in C:\Users\alexa\Documents\java-src\AOP_FunctionalInterface_Test)
2018-06-16 14:15:10.056 INFO 12456 --- [ main] com.Application : No active profile set, falling back to default profiles: default
2018-06-16 14:15:10.118 INFO 12456 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext#3c87521: startup date [Sat Jun 16 14:15:10 ICT 2018]; root of context hierarchy
2018-06-16 14:15:11.437 INFO 12456 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-06-16 14:15:11.452 INFO 12456 --- [ main] com.Application : Started Application in 1.724 seconds (JVM running for 2.524)
execution(boolean com.test.BiPredicateExample.cmp1(Integer,Integer))
true
execution(BiPredicate com.test.BiPredicateExample.getCmp())
true
2018-06-16 14:15:11.452 INFO 12456 --- [ Thread-5] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext#3c87521: startup date [Sat Jun 16 14:15:10 ICT 2018]; root of context hierarchy
2018-06-16 14:15:11.531 INFO 12456 --- [ Thread-5] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
Process finished with exit code 0
I have a very simple Spring Boot app that I'm trying to get working with some externalised configuration. I've tried to follow the information on the spring boot documentation however I'm hitting a road block.
When I run the app below the external configuration in the application.properties file does not get populated into the variable within the bean. I'm sure I'm doing something stupid, thanks for any suggestions.
MyBean.java (located in /src/main/java/foo/bar/)
package foo.bar;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
#Component
public class MyBean {
#Value("${some.prop}")
private String prop;
public MyBean() {
System.out.println("================== " + prop + "================== ");
}
}
Application.java (located in /src/main/java/foo/)
package foo;
import foo.bar.MyBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
#Autowired
private MyBean myBean;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
application.properties (located in /src/main/resources/)
some.prop=aabbcc
Log output when executing the Spring Boot app:
grb-macbook-pro:properties-test-app grahamrb$ java -jar ./build/libs/properties-test-app-0.1.0.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.1.5.RELEASE)
2014-09-10 21:28:42.149 INFO 16554 --- [ main] foo.Application : Starting Application on grb-macbook-pro.local with PID 16554 (/Users/grahamrb/Dropbox/dev-projects/spring-apps/properties-test-app/build/libs/properties-test-app-0.1.0.jar started by grahamrb in /Users/grahamrb/Dropbox/dev-projects/spring-apps/properties-test-app)
2014-09-10 21:28:42.196 INFO 16554 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext#67e38ec8: startup date [Wed Sep 10 21:28:42 EST 2014]; root of context hierarchy
2014-09-10 21:28:42.828 INFO 16554 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'beanNameViewResolver': replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]]
2014-09-10 21:28:43.592 INFO 16554 --- [ main] .t.TomcatEmbeddedServletContainerFactory : Server initialized with port: 8080
2014-09-10 21:28:43.784 INFO 16554 --- [ main] o.apache.catalina.core.StandardService : Starting service Tomcat
2014-09-10 21:28:43.785 INFO 16554 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/7.0.54
2014-09-10 21:28:43.889 INFO 16554 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2014-09-10 21:28:43.889 INFO 16554 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1695 ms
2014-09-10 21:28:44.391 INFO 16554 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2014-09-10 21:28:44.393 INFO 16554 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
================== null==================
2014-09-10 21:28:44.606 INFO 16554 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-09-10 21:28:44.679 INFO 16554 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2014-09-10 21:28:44.679 INFO 16554 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[text/html],custom=[]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest)
2014-09-10 21:28:44.716 INFO 16554 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-09-10 21:28:44.716 INFO 16554 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-09-10 21:28:44.902 INFO 16554 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2014-09-10 21:28:44.963 INFO 16554 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080/http
2014-09-10 21:28:44.965 INFO 16554 --- [ main] foo.Application : Started Application in 3.316 seconds (JVM running for 3.822)
^C2014-09-10 21:28:54.223 INFO 16554 --- [ Thread-2] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext#67e38ec8: startup date [Wed Sep 10 21:28:42 EST 2014]; root of context hierarchy
2014-09-10 21:28:54.225 INFO 16554 --- [ Thread-2] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
The way you are performing the injection of the property will not work, because the injection is done after the constructor is called.
You need to do one of the following:
Better solution
#Component
public class MyBean {
private final String prop;
#Autowired
public MyBean(#Value("${some.prop}") String prop) {
this.prop = prop;
System.out.println("================== " + prop + "================== ");
}
}
Solution that will work but is less testable and slightly less readable
#Component
public class MyBean {
#Value("${some.prop}")
private String prop;
public MyBean() {
}
#PostConstruct
public void init() {
System.out.println("================== " + prop + "================== ");
}
}
Also note that is not Spring Boot specific but applies to any Spring application
The user "geoand" is right in pointing out the reasons here and giving a solution. But a better approach is to encapsulate your configuration into a separate class, say SystemContiguration java class and then inject this class into what ever services you want to use those fields.
Your current way(#grahamrb) of reading config values directly into services is error prone and would cause refactoring headaches if config setting name is changed.
This answer may or may not be applicable to your case ... Once I had a similar symptom and I double checked my code many times and all looked good but the #Value setting was still not taking effect. And then after doing File > Invalidate Cache / Restart with my IntelliJ (my IDE), the problem went away ...
This is very easy to try so may be worth a shot
Actually, For me below works fine.
#Component
public class MyBean {
public static String prop;
#Value("${some.prop}")
public void setProp(String prop) {
this.prop= prop;
}
public MyBean() {
}
#PostConstruct
public void init() {
System.out.println("================== " + prop + "================== ");
}
}
Now whereever i want, just invoke
MyBean.prop
it will return value.
Moved no argument constructor code to PostConstruct has done the trick
for me. As it'll keep default bean loading workflow intact.
#Component
public class MyBean {
#Value("${some.prop}")
private String prop;
#PostConstruct
public void init() {
System.out.println("================== " + prop + "================== ");
}
}
Using Environment class we can get application. Properties values
#Autowired,
private Environment env;
and access using
String password =env.getProperty(your property key);
follow these steps.
1:- create your configuration class like below you can see
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Value;
#Configuration
public class YourConfiguration{
// passing the key which you set in application.properties
#Value("${some.pro}")
private String somePro;
// getting the value from that key which you set in application.properties
#Bean
public String getsomePro() {
return somePro;
}
}
2:- when you have a configuration class then inject in the variable from a configuration where you need.
#Component
public class YourService {
#Autowired
private String getsomePro;
// now you have a value in getsomePro variable automatically.
}
If you're working in a large multi-module project, with several different application.properties files, then try adding your value to the parent project's property file.
If you are unsure which is your parent project, check your project's pom.xml file, for a <parent> tag.
This solved the issue for me.
You can use Environment Class to get data :
#Autowired
private Environment env;
String prop= env.getProperty('some.prop');
Simplest solution that solved this issue for me:
Add #PropertySource annotation to the Component/Service that needs to populate #Value field:
#Service
#PropertySource("classpath:myproperties.properties")
public class MyService {
#Value("${some.prop}")
private String someProperty;
// some logic...
}
Make sure to add the properties file to the resource folder of the same module as your Service/Component.
You are getting this error because you are initializing the class with new keyword. To solve this,
first you need to create the configuration class and under this class you need to create the bean of this class.
When you will call it by using bean then it will work..
package ca.testing;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.sql.Connection;
import java.sql.DriverManager;
#Component
#PropertySource("db.properties")
public class ConnectionFactory {
#Value("${jdbc.user}")
private String user;
#Value("${jdbc.password}")
private String password;
#Value("${jdbc.url}")
private String url;
Connection connection;
public Connection getConnection(){
try {
connection = DriverManager.getConnection(url, user, password);
System.out.println(connection.hashCode());
}catch (Exception e){
System.out.println(e);
}
return connection;
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context= new AnnotationConfigApplicationContext(Config.class);
ConnectionFactory connectionFactory= context.getBean(ConnectionFactory.class);
connectionFactory.getConnection();
}
}
My application properties are picking after i have removed key word new from different class like (new Bean())