Spring MVC returning #ResponseBody content - spring

I'm new to Java and Spring MVC so apologies in advance if(when) the question is something that should be obvious. I have a simple program that is supposed to use Spring MVC and JPA to store a few messages and then display those as text on a website when the user opens localhost:8080. Some of the code is from a MOOC course that I'm taking and some of it I have modified myself.
I've got 4 files HelloMessage.java, HelloMessageRepository.java, HelloWebWithDatabaseApplication.java and HelloWebWithDatabaseController.java. Contents of the files are pasted below(I omitted the package imports).
HelloMessage.java contains the following:
#Entity
public class HelloMessage extends AbstractPersistable<Long> {
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
HelloMessageRepository.java contains the follwing:
import org.springframework.data.jpa.repository.JpaRepository;
public interface HelloMessageRepository extends JpaRepository<HelloMessage, Long> {
}
HelloWebWithDatabaseApplication.java contains this:
package sec.helloweb;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class HelloWebWithDatabaseApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(HelloWebWithDatabaseApplication.class, args);
}
}
And the last file, HelloWebWithDatabaseController.java, contains this:
#Controller
public class HelloWebWithDatabaseController {
#Autowired
private HelloMessageRepository helloMessageRepository;
#PostConstruct
public void init() {
// test data to the application
HelloMessage msg = new HelloMessage();
msg.setContent("Hello");
helloMessageRepository.save(msg);
HelloMessage msg2 = new HelloMessage();
msg2.setContent("Web");
helloMessageRepository.save(msg2);
}
#RequestMapping(value = "/", method = RequestMethod.GET)
#ResponseBody
public String list(Model model) {
model.addAttribute("content", helloMessageRepository.findAll());
return "content";
}
}
When I run the project on Netbeans and check the page it shows the text "content". I would like to print out the messages stored in the helloMessageRepository but I'm not sure on how to do that using the #ResponseBody. If I remove it and create a content.html page it works but how can I just print the messages as text?

Related

How to create objects (beens) manually?

I am new to quarkus. And I am trying to write my first quarkus application (RAX-RS service). I started with "quarkus create app" and got the initial maven project with GreetingResource.java (a JAX-RS resource).
#Path("/hello")
public class GreetingResource {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Hello from RESTEasy Reactive";
}
}
Now I want to add a constructor and a field into GreetingResource.java.
#Path("/hello")
public class GreetingResource {
private final Greeting greeting;
public GreetingResource(Greeting greeting) {
this.greeting = greeting;
}
#GET
#Produces(MediaType.TEXT_PLAIN)
public String hello() {
return greeting.say();
}
}
After that, I want to create objects manually:
public class App {
public static void main(String[] args) {
Greeting greeting = new GreetingSomeImpl();
GreetingResource greetingResource = new GreetingResource(greeting);
Server.publicResource(greetingResource); // something like this
}
}
But I do not know how to do this manually (through "new" without annotations, injections, etc.).

Sprint boot - Auto configure to call a REST service on startup

I have a requirement to create an auto-configuration for service call on spring-boot startup.
i.e., During spring-boot startup, the below service has to be called.
#PostMapping(path = "/addProduct", produces = "application/json", consumes = "application/json")
public #ResponseBody String addProduct(#RequestBody String productStr) {
..<My code>..
}
The add product requires an input like:
{
"product":"test",
"price":"10"
}
This will internally call a database service.
During startup, the json input provided in the console should be fed to this service.
I have no idea on how to achieve this. Verified a couple of Spring documentation. But those does'nt suit the requirement.
Kindly help in explaining a way or providing a right documentation to achieve this.
One way to do this is by implementing ApplicationRunner like this :
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
#Component
public class ApplicationInitializer implements ApplicationRunner {
private ProductController productController;
public ApplicationInitializer(ProductController productController) {
this.productController = productController;
}
#Override
public void run(ApplicationArguments args) throws Exception {
String productArg = args.getOptionValues("product").get(0); // Assume that you will have only one product argument
ObjectMapper mapper = new ObjectMapper();
Product product = mapper.readValue(productArg, Product.class);
String response = productController.add(product);
System.out.println(response);
}
}
The run method will be invoked at startup with arguments passed in the command line like this : java -jar yourApp.jar --product="{\"name\":\"test\", \"price\":\"15\"}".
And you need a class to map the json to an object like this :
public class Product {
private String name;
private int price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
You can also call your Controller using the RestTemplate (or WebClient) if needed :
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
#Component
public class ApplicationInitializer implements ApplicationRunner {
#Override
public void run(ApplicationArguments args) throws Exception {
String productArg = args.getOptionValues("product").get(0); // Assume that you will have only one product argument
ObjectMapper mapper = new ObjectMapper();
Product product = mapper.readValue(productArg, Product.class);
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.postForObject("http://localhost:8080/products", product, String.class);
System.out.println(response);
}
}
Such requirement can be achieved by using an init() method annotated with #PostConstruct in a bean.
e.g.
#Component
public class Foo {
#PostConstruct
public void init() {
//Call your service
}
}

Spring Boot #Component doesn't create Beans

Since according to the docs #Component registers beans for the Spring container I'm trying to create a simple example of dependency injection using the following code:
package pl.playground;
//...
#SpringBootApplication
public class PlaygroundApplication {
#Autowired
private static Building building;
public static void main(String[] args) {
building.setBuildingSize(12L);
System.out.println(building.monthlyHeatingCost());
}
}
package pl.playground.facade;
//...
#Component
public class Building {
private HeatingService service;
private Long buildingSize;
#Autowired
public Building(HeatingService service) {
this.service = service;
}
public Double monthlyHeatingCost() {
return service.getMonthlyHeatingCost(buildingSize);
}
// getters & setters...
}
package pl.playground.service;
public interface HeatingService {
Double getMonthlyHeatingCost(Long size);
}
package pl.playground.service;
//...
#Component
public class HeatingServiceImpl implements HeatingService {
private final Double CUBIC_PRICE = 2.3;
public HeatingServiceImpl() {}
#Override
public Double getMonthlyHeatingCost(Long size) {
return size * CUBIC_PRICE;
}
}
It builds and runs, but there is a NullPointerException at building.setBuildingSize(12L);. However the one below works without any issues:
//PlaygroundApplication.java
package pl.playground;
//...
#SpringBootApplication
public class PlaygroundApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Building building = context.getBean(Building.class);
building.setBuildingSize(12L);
System.out.println(building.monthlyHeatingCost());
}
}
package pl.playground.config;
//...
#Configuration
public class Config {
#Bean
public Building building(HeatingService service) {
return new Building(service);
}
#Bean
public HeatingServiceImpl heatingServiceImpl() {
return new HeatingServiceImpl();
}
}
The rest is the same as before.
Why is #Component not creating Beans?
It is working the way I think it should when used inside a #Controller of a web app, does that make a difference? How does exactly #Bean and #Component differ?
What am I failing to understand?
EDIT
Consider the following scenario:
package pl.playground;
//...
#SpringBootApplication
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
package pl.playground.controller;
//...
#Controller
public class Controller {
private Facade facade;
#Autowired
public Controller(Facade facade) {
this.facade = facade;
}
#GetMapping("/")
public String getIndexPage(Model model) {
return "index";
}
}
package pl.playground.facade;
//...
#Component
public class Facade {
private PostsService postService;
private UserService userService;
private TagService tagService;
#Autowired
public Facade(PostsService retrieve, UserService user, TagService tag) {
this.postService = retrieve;
this.userService = user;
this.tagService = tag;
}
//...
}
I don't need #Configuration here for it to work. That's my concern.
The problem with your code is that you are trying to #Autowire on a static field. You simply cannot do that. Look here: Can you use #Autowired with static fields?
It fails to work because the PlaygroundApplication class is not being created and managed by spring. The injection works only inside instances managed by spring. You can treat class annotated with #SpringBootApplication as configuration classes. Spring creates instances of those classes and injection works inside them but only on instance fields.
The second example shows the correct way to access spring beans from main method of the application.
Well. I used your original question and is working without any issues. #cezary-butler pointed out in the comments you can autowire into PlaygroundApplication but you can get hold of it easily in the static main method using context.getBean(Building.class)
#SpringBootApplication
public class PlaygroundApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(PlaygroundApplication.class);
Building building = context.getBean(Building.class);
building.setBuildingSize(12L);
System.out.println(building.monthlyHeatingCost());
}
}
Here is the sample repo https://github.com/kavi-kanap/stackoverflow-63072236
TLDR;
A Spring context needs to be created before any bean can be injected. In the first scenario, just the fact of having a #SpringBootApplication decorator does not ensure a context in the scope of the class it decorates.
SpringApplication.run(ExampleApplication.class, args); instantiates a context (and e.g. a web server among other things)
var context = new AnnotationConfigApplicationContext(Config.class); instantiates a scoped context
Thus the first example had null inside of Building as there was no context with the bean to inject.

spring-cloud-aws Spring creates message header attribute not supported by SQS

I'm using Spring Cloud AWS messaging to send/receive messages using SQS.
My code looks like this:
#SpringBootApplication
#Import(MessagingConfig.class)
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
#Configuration
public class MessagingConfig {
#Bean
public QueueMessagingTemplate queueMessagingTemplate(AmazonSQS amazonSqs, ResourceIdResolver resourceIdResolver) {
return new QueueMessagingTemplate(amazonSqs, resourceIdResolver);
}
}
The sender code is like this (wired via a controller):
#Component
public class Sender {
#Autowired
private QueueMessagingTemplate queueMessagingTemplate;
public void send(MyMessage message) {
queueMessagingTemplate.convertAndSend("testQueue", message);
}
}
I have an application.yaml file that defines the AWS parameters that seem to be correctly loaded. So, when I run this application, I get the following warning/error:
Message header with name 'id' and type 'java.util.UUID' cannot be sent as message attribute because it is not supported by SQS.
Is there something that I'm doing wrong here, or is there an issue with the way Spring creates messages for SQS?
This appears to be only a warning and does not affect the sending and/or receiving of the message. When I tested this against a real SQS queue, I could both send and receive messages.
However, when using elasticMQ on my local box as a replacement for the real SQS, it was failing to process the message. It looks like an issue with that tool rather than Spring.
How to answer the question this
The problem occurs because the constructor called MessageHeaders class
MessageHeaders class
MessageHeaders(Map<String, Object> headers) { } on line 39
And to not send the id header you need to call the constructor
MessageHeaders class
MessageHeaders(Map<String, Object> headers, UUID id, Long timestamp){} on line 43
because this constructor has the condition does not create the id header automatically
to stop sending the header id you need to override the MessageHeader and NotificationMessagingTemplate classes
class MessageHeaders
public class MessageHeadersCustom extends MessageHeaders {
public MessageHeadersCustom() {
super(new HashMap<String, Object>(), ID_VALUE_NONE, null);
}
}
class NotificationMessagingTemplate
public class NotificationMessagingTemplateCustom extends NotificationMessagingTemplate {
public NotificationMessagingTemplateCustom(AmazonSNS amazonSns) {
super(amazonSns);
}
#Override
public void sendNotification(Object message, String subject) {
MessageHeaders headersCustom = new MessageHeadersCustom();
headersCustom.put(TopicMessageChannel.NOTIFICATION_SUBJECT_HEADER, subject);
this.convertAndSend(getRequiredDefaultDestination(), message, headersCustom);
}
}
And finally, your class that will make the call need to use your implementation
package com.stackoverflow.sample.web;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.aws.messaging.core.NotificationMessagingTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
#RequestMapping("/whatever")
public class SampleController {
#Autowired
private NotificationMessagingTemplateCustom template;
#RequestMapping(method = RequestMethod.GET)
public String handleGet() {
this.template.sendNotification("message", "subject");
return "yay";
}
}
}

access to property in external file works in controller but not in domain class

I have a property set in an external application.properties file and find that I can access it in the controller but not in the domain class. I'm using SpringBoot 1.1.9 and groovy and code snippets listed below.
Can someone please explain what I'm missing here?
Thanks!
--john
//Application class used to startup
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
//Controller - property is injected
#RestController
class CityController {
#Value( '${sample.property}' )
String stringTemplate
#RequestMapping(value = "/", method = RequestMethod.GET)
public String index(HttpServletResponse response) {
return String.format(stringTemplate, 'world')
}
}
//Domain class - property does not seem to be injected
#Component
public class City {
#Value( '${sample.property}' )
String stringTemplate
String toString() {
return String.format(stringTemplate, 'world')
}
}

Resources