Spring Boot application properties #Value - java

I'm trying to create a simple Spring Boot Application, creating a GET using resttemplate.getForObject and instead of hard coding the url within the parameter, I want to create a string and leverage #Value to call the url from application.properties. I've checked videos and the board (#Value in Springboot returns null) and every time I run my JUnit test the endpoint is null. I was wondering if someone can take a look at my code and point out what am I doing incorrectly, why is #Value always null and what can I do to fix this?
#SpringBootApplication
public class NhlTeamsApplication {
public static void main(String[] args) {
SpringApplication.run(NhlTeamsApplication.class, args);
}
}
//my controller
public class NhlTeamsController {
#Autowired
private NhlTeamsService nhlTeamsService;
#RequestMapping(method = RequestMethod.GET, value = "/requestAllTeams")
public #ResponseBody String requestAllTeams() {
return nhlTeamsService.allTeamsService();
}
}
//my interface
#Component
public interface NhlTeamsService {
String allTeamsService();
}
//implementation
#Service
#Data
#Configuration
#PropertySource("classpath:application.properties")
public class NhlTeamsServiceImpl implements NhlTeamsService{
#Value("${nhl.endpoint}")
private String endpoint;
#Override
public String allTeamsService() {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForObject(getEndpoint(), String.class);
}
}
//application.properties
nhl.endpoint=https://statsapi.web.nhl.com/api/v1/teams
//JUnitTest
#Test
public void nhlAllTeamsTest(){
NhlTeamsServiceImpl nhlTeamsService = new NhlTeamsServiceImpl();
System.out.println(nhlTeamsService.allTeamsService());
}
Am I missing any annotations? Any insight is greatly appreciated.

When you running tests that time you create a new object of NhlTeamsServiceImpl.
But in spring have spring container inside that at run time spring had to initialize that object with all variable & stored.
So when you create a new object it will initialize of spring container & in that it doesn't have to initialize all required thing as #value variable
so do one thing in test case class modified as follows,
class junit{
#Autowired
public NhlTeamsServiceImpl nhlTeamsServiceImpl;
//JUnitTest
#Test
public void nhlAllTeamsTest(){
//NhlTeamsServiceImpl nhlTeamsService = new NhlTeamsServiceImpl();
System.out.println(nhlTeamsService.allTeamsService());
}
}
So it will work.
Find below the working snap.
& same working code add here https://github.com/MaheshMore4321/RunTestCases

Related

How to use the data stored in cache for another function in springboot

I have created a sample spring boot application which have a class "CacheManagement "where I have a function "getAllValue" which return the a customer object whose values I need to cache.
This class also have a function checkCache which uses the data from cache. How Can I implement this. Somebody please help me on this. I am providing the class I have written.PFB the class
#Service
public class CacheManagement {
#Cacheable(value = "service",key = "list")
public Customer getAllValue(){
Customer customer = new Customer();
System.out.println("Inside cache");
customer.setId("1");
customer.setName("Shilpa");
return customer;
}
public String checkCache() {
Customer cus = getAllValue();
System.out.println(cus.toString());
return "1";
}
}
PFB the main class:
#SpringBootApplication
public class DemoApplication implements CommandLineRunner{
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... arg0) {
CacheManagement cacheService = new CacheManagement();
cacheService.getAllValue();
cacheService.checkCache();
}
}
According to my need the sysout statement should only print once right? But my issue is it is printing twice .Means the getAllValue() call from checkCache() method is calling the function itself not fetching data from cache.
Please help me to resolve this issue.
There are multiple issues with your code.
CacheManagement cacheService = new CacheManagement(); creates an instance which is not a Spring container managed bean. The Spring magic will not work here . You should get a Spring container managed bean of cacheManagement for all your Spring framework support to kickin.
#SpringBootApplication
#EnableCaching
public class DemoApplication implements CommandLineRunner {
#Autowired
CacheManagement cacheService;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... arg0) {
cacheService.getAllValue();
cacheService.checkCache();
}
}
2.Assuming you have enabled caching using #EnableCaching ( Refer here )
#SpringBootApplication
#EnableCaching
public class DemoApplication implements CommandLineRunner{
...
}
The Spring Caching works with the help of Spring AOP and Spring AOP works on proxies. Calling getAllValue() from within a method of the same class ( here checkCache()) is called a self-invocation. Spring AOP will not be able to advice the method call in this case , as it will not go through the proxy and hence the caching logic fails to work.
To understand this concept in detail , do refer the official spring reference documentation : Understanding AOP Proxies . Read through the section starting with The key thing to understand here is that the client code inside the main(..)
The resolution to your issue is to make sure the checkValue() call goes through the proxy so that the cached result is returned. Since you need the method (checkValue()) as part of the same bean , a self reference , will do it.
You must read through the official documentation on the same topic though to understand the implications. Section starting with As of 4.3, #Autowired also considers self references for injection ..
Following code should fix the issue for you
#Service
public class CacheManagement {
#Autowired
CacheManagement self;
#Cacheable(value = "service",key = "list")
public Customer getAllValue(){
Customer customer = new Customer();
System.out.println("Inside cache");
customer.setId("1");
customer.setName("Shilpa");
return customer;
}
public String checkCache() {
Customer cus = self.getAllValue();
System.out.println(cus.toString());
return "1";
}
}
Read more on caching here

How to instantiate object(Jdbc template) inside Hazelcast Map store

I'm trying to Autowire jdbc template inside mapStore.. but I'm getting null pointer exception.
I worked on so many examples but sill not able to resolve this issue..
Here is my main class
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class TestCacheApplication {
public static void main(String[] args) {
SpringApplication.run(TestCacheApplication.class, args);
System.err.println("......running successfully......");
}
}
Here is my cache configured code
#Component
public class CacheConfig {
#Bean
public static Config config() {
System.err.println("config class");
Config config = new Config();
config.setInstanceName("hazelcast");
MapConfig mapCfg = new MapConfig();
mapCfg.setName("first-map");
mapCfg.setBackupCount(2);
mapCfg.setTimeToLiveSeconds(300);
MapStoreConfig mapStoreCfg = new MapStoreConfig();
mapStoreCfg.setClassName(DataMapStore .class.getName()).setEnabled(true);
mapCfg.setMapStoreConfig(mapStoreCfg);
config.addMapConfig(mapCfg);
return config;
}
}
and TblRepo implementation
#Service
public class DataTblRepoImpl implements DataTblRepo {
#Autowired
private JdbcTemplate jdbcTemplate;
#Override
public void save(String id, String name) {
Object[] params = new Object[] { id, name };
int[] types = new int[] { Types.VARCHAR, Types.VARCHAR };
String insertSql = "INSERT INTO public.person(id, name) VALUES(?, ?)";
jdbcTemplate.update(insertSql, params, types);
}
and TblRepo interface I have annotated with #Repository annotation..
And My map store class
#SpringAware
public class DataMapStore implements MapStore<String, ModelClass>{
#Autowired
DataTblRepo dataTblRepo;
#Override
public void store(String key, ModelClass value) {
dataTblRepo.save(value.getId(), value.getName());
}
//remaining methods will come here
}
and Controller
#RestController
#CrossOrigin(origins = "*")
#RequestMapping("/api/v1")
public class DataController {
#Autowired
DataService dataService;
HazelcastInstance hazelCast = Hazelcast.getHazelcastInstanceByName("hazelcast");
#PostMapping("/{test}")
public String saveDatafrom(#RequestBody ModelClass model) {
hazelCast.getMap("first-map").put(model.getId(), model);
return "stored";
}
}
Here is the program flow.. When I start the application, first Cacheconfig class will run.
In the controller when I perform the map.put() operation, data will go to the DataMapStore class and call the store method to save the data in database..since DataTblRepo is null so operation is failing at the store method itself..*
I tried adding #component on the DataMapStore class also
but in my case I'm getting this error
"message": "Cannot invoke "com.example.demo.repo.DataTblRepository.save(String, String)" because "this.dataTableRepo" is null",
I saw this same issue in many platforms also but still not able to resolve this issue.
Any suggestions would be very helpful
SpringAware is for Hazelcast distributed objects (cf. documentation).
The MapStore in your example is not a distributed object but a simple plain object. It should be managed by Spring itself. You should replace the #SpringAware annotation by a Spring #Component annotation.
The next issue is that your map store configuration makes Hazelcast responsible to instantiate the MapStore. If this happens, you won't benefit from Spring's Dependency Injection mechanism. You should directly set the instance created by Spring.
Replace SpringAware by Component
#Component
public class DataMapStore implements MapStore<String, ModelClass> {
// ...
}
Use the Spring-configured MapStore instance
#Bean
public Config config(DataMapStore mapStore) { // Ask Spring to inject the instance
// ...
MapStoreConfig mapStoreCfg = new MapStoreConfig();
mapStoreCfg.setImplementation(mapStore); // Use it
mapCfg.setMapStoreConfig(mapStoreCfg);
config.addMapConfig(mapCfg);
return config;
}
I also removed the static keyword on the config() method.
Note that this way of using MapStore couples it with the "client" code. This means you need to use Hazelcast embedded. For more information about embedded mode vs. client/server, please check the documentation related to topology.

Can't achieve dependency injection oustide a controller in Spring Booot

I am new at spring MVC framework and i am currently working in a web application that uses a session scoped bean to control some data flow.
I can access these beans in my application context using #Autowired annotation without any problem in the controllers. The problem comes when I use a class in service layer that does not have any request mapping (#RequestMapping, #GetMapping nor #PostMapping) annotation.
When I try to access the application context directly or using #Autowired or even the #Resource annotation the bean has a null value.
I have a configuration class as follow:
#Configuration
#EnableAspectJAutoProxy
#EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class, basePackages = "com.quantumx.nitididea.NITIDideaweb.repository")
public class AppConfig implements WebMvcConfigurer {
#Bean (name = "lastTemplate")
#SessionScope
public LastTemplate getlastTemplate() {
return new LastTemplate();
}
//Some extra code
}
The POJO class is defined as :
public class LastTemplate {
private Integer lastId;
public LastTemplate(){
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}
The I have a Test class that is annotated as service and does not have any request mapping annotated method:
//#Controller
#Service
public class Test {
// #Autowired
// private ApplicationContext context;
// #Autowired
#Resource(name = "lastTemplate")
public LastTemplate lastTemplate;
// #Autowired
// public void setLastTemplate(LastTemplate lastTemplate) {
// this.lastTemplate = lastTemplate;
// }
public Test() {
}
// #RequestMapping("/test")
public String testing() {
// TemplateForma last = (TemplateForma) context.getBean("lastInsertedTemplate");
// System.out.println(last);
System.out.println(lastTemplate);
// System.out.println(context.containsBean("lastTemplate"));
// System.out.println(context.getBean("lastTemplate"));
System.out.println("Testing complete");
return "Exit from testing method";
// return "/Messages/Success";
}
}
As you can see, there is a lot of commented code to show all the ways i have been trying to access my application context, using an Application context dependency, autowiring, declaring a resource and trying with a request mapping. The bean is null if no controller annotation and request mapping method is used and throws a java null pointer exception when I use the context getBean() methods.
Finally I just test my class in a controller that i have in my app:
#RequestMapping("/all")
public String showAll(Model model) {
Test test = new Test();
test.testing();
return "/Administrator/test";
}
Worth to mention that I also tried to change the scope of the bean to a Application scope and singleton, but it not worked. How can access my application context in a service class without mapping a request via controller?
Worth to mention that I also tried to change the scope of the bean to a Application scope and singleton, but it not worked
It should have worked in this case.
How can access my application context in a service class without mapping a request via controller?
Try one of these :-
#Autowired private ApplicationContext appContext;
OR
Implement ApplicationContextAware interface in the class where you want to access it.
Edit:
If you still want to access ApplicationContext from non spring managed class. Here is the link to article which shows how it can be achieved.
This page gives an example to get spring application context object with in non spring managed classes as well
What worked for me is that session scoped bean had to be removed in the application configuration declaration and moved to the POJO definition as follows:
#Component
#SessionScope
public class LastTemplate {
private Integer lastId;
public LastTemplate(){
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}
The I just call the bean using #Autowired annotation.

Use Spring Service in simple class

I'm working in a spring application and I'm trying to use a service class which works perfectly but not inside a simple class.
I suppose that this has to be with bean injection?? or maybe to access spring application context??
What's the best approach to solve this?
Here's my class:
public class RequestDatosFactura
{
JSONObject request;
private Factura factura;
#Autowired
private BitacoraService bitacoraServ;
public RequestDatosFactura(String req) throws JSONException {
this.request = new JSONObject( req );
this.factura = crearFactura();
}
//methods inside class...
This simple class tries to autowired BitacoraService, but when I instantiate, say for example into a spring controller it does well but the BitacoraService is not working.
Any guide or help will be very appreciated
The solution here is to get the service injected in the controller then pass that instance to your regular class, something like:
#Controller
class YourController {
#Autowired
private BitacoraService bitacoraService;
public void someMethod() {
RequestDatosFactura requestDatosFactura = new RequestDatosFactura(bitacoraService);
// use "requestDatosFactura"
}
}
class RequestDatosFactura {
// ...
private final BitacoraService bitacoraService;
public RequestDatosFactura(final BitacoraService bitacoraService) {
this.bitacoraService = bitacoraService;
 }
// ...
}

Spring boot testing of a rest client using #RestClientTest

I am using spring boot 1.5.8 and want to test my client:
#Component
public class RestClientBean implements RestClient {
private Map<String, RestTemplate> restTemplates = new HashMap<>();
#Autowired
public RestClientBean(RestTemplateBuilder builder, SomeConfig conf) {
restTemplates.put("first", builder.rootUri("first").build();
restTemplates.put("second", builder.rootUri("second").build();
}
}
with the following test:
#RunWith(SpringRunner.class)
#RestClientTest(RestClient.class)
public class RestClientTest {
#Autowired
private RestClient client;
#Autowired
private MockRestServiceServer server;
#TestConfiguration
static class SomeConfigFooBarBuzz {
#Bean
public SomeConfig provideConfig() {
return new SomeConfig(); // btw. not sure why this works,
// but this is the only way
// I got rid of the "unable to load
// SomeConfig auto-wire" or something like this :)
// Anyway not my main issue
// EDIT: just realized that the whole
// #TestConfiguration part can be avoided by
// adding SomeConfig.class to the classes in the
// #RestClientTest annotation
}
}
#Before
public void setUp() throws Exception {
server.expect(requestTo("/somePath")) // here an exception is thrown
// (main issue)
.andRespond(withSuccess("<valid json>", MediaType.APPLICATION_JSON));
}
}
The exception is very clear:
java.lang.IllegalStateException: Unable to use auto-configured
MockRestServiceServer since MockServerRestTemplateCustomizer has been bound to
more than one RestTemplate
But is it somehow possible to get this tested or is it not allowed to instantiate two different rest templates in one client class?
I have just the need to use the first rest template in some cases and the second one in some others.
After some days of investigation and communication with spring folks via GitHub I found a solution for me and not receiving an answer here means my solution might be valuable for someone:
#RunWith(SpringRunner.class)
#RestClientTest(RestClient.class)
public class RestClientTest {
#Autowired
private RestClient client;
private MockRestServiceServer firstServer;
private MockRestServiceServer secondServer;
private static MockServerRestTemplateCustomizer customizer;
#TestConfiguration
static class RestTemplateBuilderProvider {
#Bean
public RestTemplateBuilder provideBuilder() {
customizer = new MockServerRestTemplateCustomizer();
return new RestTemplateBuilder(customizer);
}
}
#Before
public void setUp() {
Map<RestTemplate, MockRestServiceServer> servers = customizer.getServers();
// iterate and assign the mock servers according to your own strategy logic
}
#Test
public void someTest() {
firstServer.expect(requestTo("/somePath"))
.andRespond(withSuccess("some json body"),
MediaType.APPLICATION_JSON));
// call client
// verify response
}
Basically specify a number of mock servers same as the number of rest templates you use in your client code, then specify a test configuration providing a rest builder with a customizer, so that your client code's rest templates will be built via this customized builder. Then use the customizer to get the mock servers bounded to the created rest templates and define expectations on them as you want.

Categories

Resources