Spring boot configuration problem with YAML files - spring

I have the following yaml file
proj:
sms:
gateway:
username: d
password: erer
signature: e
url: http://link.somelink.com
actionKeyMap:
OTP_GENERATOR: Value1
CREATE_USER: Value2
I'm trying to bind the actionKeyMap/gateway property into a Java map and it doesn't works.
I've tried the following code
#Component
#ConfigurationProperties(prefix="proj.sms")
public class MessageResolver {
//#Value("${proj.sms.actionKeyMap}")
private Map<String, String> actionKeyMap;
ConfigurationProperties or Value annotation doesn't works.

Adding code as discussed in comments
Application.yml
proj:
sms:
gateway:
username: d
password: erer
signature: e
url: http://link.somelink.com
actionKeyMap:
OTP_GENERATOR: Value1
CREATE_USER: Value2
Spring boot Application.java
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
//#EnableConfigurationProperties
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
MessageResolver.java
package hello;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
#Component
#ConfigurationProperties(prefix="proj.sms")
public class MessageResolver {
//#Value("${proj.sms.actionKeyMap}")
private Map<String, String> actionKeyMap;
#Value("${proj.sms.actionKeyMap.OTP_GENERATOR}")
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
public Map<String, String> getActionKeyMap() {
return actionKeyMap;
}
public void setActionKeyMap(Map<String, String> actionKeyMap) {
this.actionKeyMap = actionKeyMap;
}
}
GreetingController.java
package hello;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#Autowired
MessageResolver messageResolver;
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value="name", defaultValue="World") String name) {
System.out.println(messageResolver.getTest());
System.out.println(messageResolver.getActionKeyMap());
return new Greeting(counter.incrementAndGet(),
String.format(template, name));
}
}
Greeting.java (in case you try to build complete project)
package hello;
public class Greeting {
private final long id;
private final String content;
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
URL http://localhost:8080/greeting
Console Output from GreetingController (sop on line 22 & 23)
Value1
{OTP_GENERATOR=Value1, CREATE_USER=Value2}

You can get information with this link on how to solve your problem: How to inject a Map using the #Value Spring Annotation?
it will look something like:
actionKeyMap: '{
OTP_GENERATOR: "value1",
CREATE_USER: "value2"
}'

Try :(colon) intead of .(dot)
#Component
#ConfigurationProperties(prefix="proj:sms")
public class MessageResolver {
#Value("${proj:sms:actionKeyMap}")
private Map<String, String> actionKeyMap;

You just need to declare Gateway and ActionKeyMap class to match the ymlproperty. Or you can read the Spring Boot reference here, link.
#Component
#ConfigurationProperties(prefix="proj.sms")
public class MessageResolver {
private Gateway gateway;
private ActionKeyMap actionKeyMap;
//getter/setter
}
``
public class Gateway {
private String username;
private String password;
private String signature;
private String url;
//getter/setter
}
``
public class ActionKeyMap {
private String OTP_GENERATOR;
private String CREATE_USER;
//getter/setter
}

Related

SpringBoot #Autowired NullPointerException when calling method from service class

I'm learning Spring Boot. I'm trying to fetch a value from an ArrayList from the service class by calling its method. But I got a NullPointerException. I add both #Autowired and #Service annotations, it doesn't work. I also looked up some topics about Loc container but it doesn't seem to help.
Here is my MainController:
package com.example.ecommerce;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.ArrayList;
#Controller
public class MainController {
#Autowired
private ProductService productService;
//I'm getting a NullPointerException here when I call the method "getAllProduct"
ArrayList<Product> products = productService.getAllProduct();
#GetMapping("/")
public String home(){
return "home";
}
#GetMapping("/products")
public String product(Model model){
model.addAttribute("products",products);
return "product";
}
}
Here is my service class:
package com.example.ecommerce;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
#Service
public class ProductService {
ArrayList<Product> products = new ArrayList<>(Arrays.asList(
new Product("iPhone 4","Apple",1000),
new Product("iPhone 5","Banana",2000),
new Product("iPhone 6","Orange",3000)
));
public ArrayList<Product> getAllProduct(){
return products;
}
}
Here is the model class Product:
package com.example.ecommerce;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
private String brand;
private double price;
public Product() {
}
public Product(String name, String brand, double price) {
this.name = name;
this.brand = brand;
this.price = price;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
#Override
public String toString() {
return "Product{" +
"id=" + id +
", name='" + name + '\'' +
", brand='" + brand + '\'' +
", price=" + price +
'}';
}
}
Edit: was able to fix it by adding a constructor in the MainController
private final ProductService productService;
private final ArrayList<Product> products;
#Autowired
public MainController(ProductService productService) {
this.productService = productService;
this.products = productService.getAllProduct();
}
The service method is called before the dependency is injected. That's why you are getting NPE. Try using constructor dependency injection
#Controller
public class MainController {
public MainController(ProductService productService) {
this.productService = productService;
}
private ProductService productService;
//I'm getting a NullPointerException here when I call the method "getAllProduct"
ArrayList<Product> products = productService.getAllProduct();
#GetMapping("/")
public String home() {
return "home";
}
#GetMapping("/products")
public String product(Model model) {
model.addAttribute("products", products);
return "product";
}
}
Or call the service method inside the controller method
#Controller
public class MainController {
#Autowired
private ProductService productService;
#GetMapping("/")
public String home() {
return "home";
}
#GetMapping("/products")
public String product(Model model) {
model.addAttribute("products", productService.getAllProduct());
return "product";
}
}
your products are never getting initialized in product service class, I would suggest you to try this in ProductService class
#Service
public class ProductService {
ArrayList<Product> products;
ProductService()
{
this.products= new ArrayList<>(Arrays.asList(
new Product("iPhone 4","Apple",1000),
new Product("iPhone 5","Banana",2000),
new Product("iPhone 6","Orange",3000)
));
}
public ArrayList<Product> getAllProduct(){
return products;
}
}
Nice, always use constructor injection instead of field injection. Im just relaying the Spring recommendation. Which is the most adopted conversion lately :)

Spring boot application failed to start with exception org.springframework.beans.factory.NoSuchBeanDefinitionException:

I am using spring boot v2.5.2. Below is my folder structure and code. This is simple test project.
My folder structure:
RESTController Class:
package com.user.UserManagementSystem.controller;
import com.user.UserManagementSystem.model.User;
import com.user.UserManagementSystem.service.UserServiceImpl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/api/v1/")
public class UserController {
#Autowired
private UserServiceImpl userRepository;
#GetMapping("/getAllUsers")
public List<User> getAllUsers() {
return userRepository.getUsers();
}
#GetMapping("/")
public String home() {
return "Hello";
}
}
User.java
package com.user.UserManagementSystem.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name= "Users")
public class User {
public User() {
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name ="userName")
private String userName;
#Column(name ="name")
private String name;
#Column(name ="language")
private String language;
#Column(name ="mobileNumber")
private int mobileNumber;
public User(String userName, String name, String language, int mobileNumber) {
this.userName = userName;
this.name = name;
this.language = language;
this.mobileNumber = mobileNumber;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public int getMobileNumber() {
return mobileNumber;
}
public void setMobileNumber(int mobileNumber) {
this.mobileNumber = mobileNumber;
}
}
UserRepository.java
package com.user.UserManagementSystem.repository;
import com.user.UserManagementSystem.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface UserRepository extends JpaRepository<User, Long>{
}
UserService.java
package com.user.UserManagementSystem.service;
import com.user.UserManagementSystem.model.User;
import java.util.List;
public interface UserService {
List<User> getUsers();
User getUserById(Long id);
User addUser(User user);
void deleteUser(Long id);
}
UserServiceImpl.java
package com.user.UserManagementSystem.service;
import com.user.UserManagementSystem.repository.UserRepository;
import com.user.UserManagementSystem.model.User;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class UserServiceImpl implements UserService{
#Autowired
UserRepository userRepository;
#Override
public List<User> getUsers() {
return userRepository.findAll();
}
#Override
public User getUserById(Long id) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public User addUser(User user) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void deleteUser(Long id) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}
UserManangmentSystemApplication.java
package com.user.UserManangmentSystem;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication(scanBasePackages = {"com.user.UserManangmentSystem", "com.user.UserManagementSystem.controller", "com.user.UserManagementSystem.repository", "com.user.UserManagementSystem.service"})
//#SpringBootApplication
public class UserManangmentSystemApplication {
public static void main(String[] args) {
SpringApplication.run(UserManangmentSystemApplication.class, args);
}
}
application.properties:
spring.datasource.url=jdbc:mariadb://localhost:3306/ums
spring.datasource.username=ums
spring.datasource.password=ums
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDB53Dialect
server.port=8888
debug=true
When it build the project i am getting :
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.user.UserManagementSystem.repository.UserRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1790) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1346) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:657) ~[spring-beans-5.3.8.jar:5.3.8]
Thanks in Advance.
Typo in your base package name which makes packages different.
Change
com.user.UserManangmentSystem
To
com.user.UserManangementSystem
Correct management spelling.
You have correct package structure it will collect all bean within base package and sub package also. No need explicitly mention package scan. If you have any spell mistakes then that error will occur.

ContextConfiguration does not inject config file when running the Test

I have this configuration class in a maven project:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import lombok.Data;
#Data
#Configuration
public class SmsConfig {
#Value("${sms.domainId}")
private String domainId;
#Value("${sms.gateway.url}")
private String gatewayUrl;
#Value("${sms.cmd}")
private String cmd;
#Value("${sms.login}")
private String login;
#Value("${sms.passwd}")
private String passwd;
}
and this other class
Service("smsService")
public class AltiriaSMSRestServiceImpl implements SmsService {
private final SmsConfig smsConfig;
public AltiriaSMSRestServiceImpl(SmsConfig smsConfig) {
this.smsConfig = smsConfig;
}
#Override
public boolean sendSMS(String msg, String to) throws Exception {
...
}
...
}
That I want to test using this file:
#ContextConfiguration(classes = { SmsConfig.class })
#RunWith(MockitoJUnitRunner.class)
public class AltiriaSMSRestServiceImplTest {
#InjectMocks
private AltiriaSMSRestServiceImpl smsService;
#Test
public void testSendSMS() throws Exception {
smsService.sendSMS("this is a test", "+34653776498");
}
}
but I have a nullpointerException because smsConfig is null when running the test
You should use spring runner to run the test case so that spring beans can be injected.
#RunWith(SpringJUnit4ClassRunner.class)

Spring-boot batch file read and send data to Kafka

I am having trouble sending data from CSV file to Kafka. Here is my code for writer.java for batch processing
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder.In;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import com.codenotfound.kafka.repository.*;
import java.util.*;
import com.codenotfound.kafka.Car;
import com.codenotfound.kafka.producer.Sender;
public class Writer implements ItemWriter<Car>{
private final Repository repo;
public Writer(Repository repo) {
this.repo = repo ;
}
#Override
public void write(List<? extends Car> car) throws Exception {
repo.save(car);
}
}
So instead of repo.save(car), I want this car class details to be sent to Kafka.
Here is my Car class and Repository interface respectively
#Entity
#Table(name = "Car")
public class Car {
private String make;
private String manufacturer;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
public Car() {
//super();
}
public Car(String make, String manufacturer) {
super();
this.make = make;
this.manufacturer = manufacturer;
}
public String getMake() {
return make;
}
public void setMake(String make) {
this.make = make;
}
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#Override
public String toString() {
return "Car [make=" + make + ", manufacturer=" + manufacturer + ", id=" + id + "]";
}
}
and Repository class
public interface Repository extends CrudRepository<Car, Long>,CustomRepository {
}
My Sender file for Kafka is:
package com.codenotfound.kafka.producer;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import com.codenotfound.kafka.Car;
public class Sender {
private static final Logger LOGGER = LoggerFactory.getLogger(Sender.class);
#Value("${topic.json}")
private String jsonTopic;
#Autowired
private KafkaTemplate<String, Car> kafkaTemplate;
public void send(Car car) {
LOGGER.info("sending car='{}'", car.toString());
kafkaTemplate.send(jsonTopic, car);
}
}
Please suggest to me a method to send data from a CSV file to my Kafka.
it seems you already have all the pieces of the puzzle laid out for you. What you need to do is change your ItemWriter using your Sender class, so you'd have something like this:
#Component
public class Writer implements ItemWriter<Car> {
#Value("${topic.json}")
private String jsonTopic;
#Autowired
private KafkaTemplate<String, Car> kafkaTemplate;
#Override
public void write(List<? extends Car> cars) throws Exception {
cars.forEach(car -> kafkaTemplate.send(jsonTopic, car));
}
}
and in your job configuration, you need to autowire it like this (the code below is simplified, just showing how to declare the writer in the step):
#Configuration
#EnableBatchProcessing
public class BatchConfiguration {
#Autowired
public StepBuilderFactory stepBuilderFactory;
#Autowired
private Writer carWriter;
#Bean
public Step myStep() {
this.stepBuilderFactory.get("myStep")
.<Car,Car> chunk(10)
.reader(reader())
.writer(carWriter) // don't use new here
.build();
}
}

SpringData: How Map AngularJS page with #RepositoryRestResource?

I follow this tutorial about Data Accessing:
http://spring.io/guides/gs/accessing-data-rest/
So, I have this structure
http://i.stack.imgur.com/wUnrx.png
"Project" is the Model
package Serveur;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
private String mail;
private String password;
public long getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMail() {
return mail;
}
public void setMail(String mail) {
this.mail = mail;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
And the interface "ProjectRepository", which implemented directly by the "#RepositoryRestResource" annotation
package Serveur;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
#RepositoryRestResource(collectionResourceRel = "jsonProjects", path = "jsonProjects")
public interface ProjectRepository extends CrudRepository<Project, Integer> {
List<Project> findById(#Param("name") String name);
}
The "Home Controller" contains a #RequestMapping Annotation and redirects the user to "index" page :
package Serveur;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
#Controller
public class HomeController {
#RequestMapping("/")
public String index()
{
return "index";
}
}
But, when I connect to localhost:8080/ , the index page does not display but this:
{
_links: {
jsonProjects: {
href: "http://localhost:8080/jsonProjects{?page,size,sort}",
templated: true
}
}
}
The problem is that the #RepositoryRestResource annotation binds to "/". To fix your issue, edit Application.java and add this function:
#Override
protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
super.configureRepositoryRestConfiguration(config);
try {
config.setBaseUri(new URI("/api"));
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
Your HomeController will not bind to "/" and your API's will be available at "/api"
Here is the answer:
By default all Repository search function comes after search keyword. So in your case URL will be something like:
http://localhost:PORT/APP/jsonProjects/search/findById?name=NAME
See my working example and blog for the same:
http://sv-technical.blogspot.com/2015/11/spring-boot-and-repositoryrestresource.html
https://github.com/svermaji/Spring-boot-with-hibernate-no-controller
Hope this helps.

Resources