Spring Boot doc is out of date - spring-boot

https://spring.io/guides/gs/spring-boot/
This is out of date as far as I can tell.
The github repo didn't exist so I used the initilizr mentioned beforehand and imported that into intellij.
then I tried creating a new java class like the first part of the tutorial says in https://spring.io/guides/gs/spring-boot/#_create_a_simple_web_application
but straight out the bat, the imports in the provided code fail :
package hello;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
#RestController
public class HelloController {
#RequestMapping("/")
public String index() {
return "Greetings from Spring Boot!";
}
}
I googled around a bit and apparently (but I'm probably misunderstanding.) RestController is deprecated in favor of Controller?
what about RequestMapping then?
how do I set up a basic test rest service.
PS: in the initilizr (https://start.spring.io/), I chose Spring Boot 2.0.0 M6

in your pom.xml file, change : the very first <artifactId> line to :
<artifactId>spring-boot</artifactId>
it might have been something different (for me it was <artifactId>spring-boot-starter-parent</artifactId>).
with this you imports should be available.

Related

Spring Boot Test with Cucumber and Mockito

I recently started to use Cucumber to implement a set of behavior driven tests for a Java project based on Spring Boot. I would like to call REST endpoints of this application using something like REST Assured or a custom REST client and for external systems and database I would like to setup some mock with Mockito, as I have already done with unit tests.
But I didn't find a complete working solution that I can apply to use Mockito beans in my Cucumber steps, for example to simulate a possible response from database queries.
I found a lot of posts of people over the years that had similar problems with different versions of Cucumber/Junit/Spring but I don't understand if it exists a right way to make these tools working together because I didn't a single complete example related to these tools together. Can anyone share experiences (versions/examples) in real world projects using Spring Boot Test, Cucumber and Mockito?
I finally found the solution. You can start the Cucumber test suite with a class like this
import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME;
import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;
import org.junit.platform.suite.api.ConfigurationParameter;
import org.junit.platform.suite.api.IncludeEngines;
import org.junit.platform.suite.api.SelectClasspathResource;
import org.junit.platform.suite.api.Suite;
#Suite
#IncludeEngines("cucumber")
#SelectClasspathResource("bdd")
#ConfigurationParameter(key = PLUGIN_PROPERTY_NAME,
value = "pretty")
#ConfigurationParameter(key = PLUGIN_PROPERTY_NAME,
value = "usage")
#ConfigurationParameter(key = PLUGIN_PROPERTY_NAME,
value = "html:target/cucumber-reports")
#ConfigurationParameter(key = GLUE_PROPERTY_NAME,
value = "myproject.cucumber.glue")
public class RunCucumberTest {
}
In the glue folder you can create a Spring Boot Test that will create the Spring Context and create the bridge between Spring Boot and Cucumber worlds thanks to CucumberContextConfiguration
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import io.cucumber.spring.CucumberContextConfiguration;
import myproject.repository.MyEntityRepository;
#CucumberContextConfiguration
#SpringBootTest
#ActiveProfiles("test")
public class SpringBootTestStarter {
#MockBean
private MyEntityRepository myEntityRepository;
}
Here you can use Mockito MockBean annotation and in the steps related to Cucumber scenarios you can now use this bean as a mock

Spring boot custom starter and Spring Data JPA - How to correctly provide repositories on my own custom autoconfigure/starter module

I am trying to write an autoconfigure / starter module for one of my project. This module handles the persistence through Spring Data JPA. It aims at providing several spring data repositories.
Right now, my autoconfiguration looks like this:
#Configuration(proxyBeanMethods = false)
#AutoConfigureAfter(JpaRepositoriesAutoConfiguration::class)
#EnableJpaRepositories(basePackageClasses = [ItemRepository::class])
#EntityScan(basePackageClasses = [ItemRepository::class])
class DomainPersistenceDataJpaAutoConfiguration() {
}
As stated in spring boot reference documentation, auto configuration should not enable component scanning, although #EnableJpaRepositories uses component scanning.
What would be a good alternative approach? Is there any example of existing spring boot start which provides repositories implementation I could consult?
What you're looking for is probably
AutoConfigurationPackages#register method, I think the common approach would be to implement ImportBeanDefinitionRegistrar and import this implementation in your autoconfiguration using #Import. A very elegant solution can be seen in Axon framework's RegisterDefaultEntities annotation and DefaultEntityRegistrar which it imports. This way your packages will be included in jpa and entity scans.
Edit: Adding actual example since as the review pointed out, the links might change over time.
In your case the ImportBeanDefinitionRegistrar could look something like this:
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class StarterEntityRegistrar implements ImportBeanDefinitionRegistrar {
#Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, ItemRepository.class.getPackageName());
}
}
and your autoconfiguration would be just:
#Configuration(proxyBeanMethods = false)
#AutoConfigureAfter(JpaRepositoriesAutoConfiguration.class)
#Import(StarterEntityRegistrar.class)
class DomainPersistenceDataJpaAutoConfiguration() {
}

Odd Spring Boot #Autowired error on a repository NOT due to wrong project structure or missing annotations

I'm asking this question with a risk of having it marked as duplicate, but I honestly don't think it is, so here it goes.
I'm having trouble running a basic Spring Boot application. The error I'm getting is this:
Parameter 0 of constructor in com.example.demo.service.EventService required a bean of type 'com.example.demo.dao.EventRepository' that could not be found.
Now this seems like a pretty common error (and question on SO) that should point the developer to check the following issues:
Most commonly the project structure is wrong (or let's say 'custom') and the bean is not getting scanned because of hierarchy [Questions: this, this, this, this and probably many more]. Solution should be to place the SpringBootApplication in the top package and all the components in its sub-packages so it could scan everything properly or to custom-define the packages that need to be scanned.
The bean was not defined, i.e. there's a missing annotation of type #Service, #Repository etc. so the bean is not being created.
Another reason might be using two different ways of defining beans (as posted here). I'm using purely annotation-style defining, so I think I should be good here.
Also, this question has an answer mentioning to exclude the autoconfiguration of JPA repositories from application.properties file, but I don't have this configuration set.
There were also a couple of questions/answers mentioning issues with dependencies and pom.xml file which fixed this problem, but my pom.xml is a pretty basic file created from Spring Initializr, so again, I don't think this is the solution.
The error message also says:
Consider defining a bean of type 'com.example.demo.dao.EventRepository' in your configuration.
The 'missing bean' is this repository:
package com.example.demo.dao;
import com.example.demo.entity.Event;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface EventRepository extends JpaRepository<Event, Integer> {
}
You can see it has #Repository annotation (although I've created repositories without this annotation before and it worked fine so I don't think this is required, but I added it now just in case this was the issue), it extends JpaRepository so it should be a valid repository and it's inside com.example.demo.dao package.
The class that's autowiring this is a service:
package com.example.demo.service;
import com.example.demo.dao.EventRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class EventService {
EventRepository eventRepository;
#Autowired
public EventService(EventRepository eventRepository) {
this.eventRepository = eventRepository;
}
}
I'll also provide the main application so you can see its package is com.example.demo which is a parent package for both the service and the repository:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
I've also tried rebuilding the project and the infamous "closing and reopening" of IntelliJ, because it was known for sometimes acting stupid in my experience and this would solve it, but not this time.
Also as a side note, I've successfully created these kinds of projects before, so I'm really not sure what's the issue right now.
Am I missing something obvious here? What else can I check?
EDIT:
Here's the entity class as well (generated by an IDE tool):
package com.example.demo.entity;
import javax.persistence.*;
import java.sql.Timestamp;
import java.util.Objects;
#Entity
public class Event {
private int id;
private String name;
private Timestamp dateFrom;
private Timestamp dateTo;
// getters & setters abstracted
}
Turns out it was an issue with pom.xml after all.
I've added the JPA dependency later and accidentally added the wrong one. My pom.xml had
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
instead of
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Apparently the IDE tool used for generating entities from tables was missing javax.persistence so it added that jar manually through lib folder. Everything looked fine to IntelliJ, but something was messed between dependencies.
Anyway, I changed the dependency in pom.xml and removed the extra jar added. Now everything works fine.

Spring Boot MongoRepository unit testing using pre-installed MongoDB

I have a regular Spring Boot application (1.3.2) with MongoDB using MongoRepository.
I would like to write an integration test for one of my endpoints that gets the data from the MongoDB. As far as I see from the Spring Boot 1.3 Release Notes Spring has auto-configuration for Embedded MongoDB (de.flapdoodle.embed.mongo). However, I cannot figure out from Spring and flapdoodle documentation on how to write an integration test that would use already installed version of MongoDB on my filesystem.
So far my integration test looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(Application.class) // my application class
#WebAppConfiguration
public class IntegrationTest {
#Autowired
private MyRepository myRepository;
#Before
public void setup() {
myRepository.save(new MyEntity());
}
#Test
public void test() {
// here I will fire requests against the endpoint
}
}
I have added two dependencies with test scope: spring-boot-starter-test and de.flapdoodle.embed:de.flapdoodle.embed.mongo. So when I run the test, I can see that flapdoodle attempts to download version of MongoDB, but fails since I am behind the proxy. But I do not want to download any versions, I want it to use my locally installed MongoDB. Is it possible to do this?
If you want to use your locally installed MongoDB (not recommended, since then the tests depend on a particular DB that can get into a dirty state), then you shouldn't be pulling in the embedded MongoDB.
I believe this config will do what you're asking for, though (seems to work in my Spring Boot 1.3.5 test):
import java.net.UnknownHostException;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import com.mongodb.MongoClient;
#EnableAutoConfiguration(exclude = MongoAutoConfiguration.class)
#Configuration
public class TestConfig
{
#Primary
#Bean
MongoClient mongoClient()
{
try
{
return new MongoClient("localhost", 27017);
}
catch (UnknownHostException e)
{
throw new RuntimeException(e);
}
}
}
However, I suspect you would be better off properly configuring the proxy and using the embedded mongoDB in your tests. See this answer for hints on how to do that.

HelloWorld EJB dependency injection

I cannot get #EJB dependency injection to work. I use Linux and Maven as a build tool.
For the source code, IDE and app server I like to use the alternatives that makes it as simple as possible. I gave it a try with Glassfish 3.1.2.2, NetBeans 7.2 but no luck. I haven't done any configuration in Glassfish.
Here is some example source code, but any code that works will be helpful. Also any ideas about how to debug these kind of problems will be appreciated. It seems like every beginner is having them...
LinkResource.java
package se.xyz.webapp;
import javax.ejb.EJB;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import se.xyz.server.LinkService;
#Path("links/{username}")
public class LinkResource {
#EJB
LinkService service;
#GET
#Produces("text/plain")
public String link(#PathParam("username") #DefaultValue("NoName") String name ) {
return service.store(name); // Always nullpointer exception here!
}
}
LinkServiceImpl.java
package se.xyz.server;
import javax.ejb.Stateless;
#Stateless
public class LinkServiceImpl implements LinkService {
public String store(String name)
{
return "From eJB";
}
}
LinkService.java
package se.xyz.server;
import javax.ejb.Local;
#Local
public interface LinkService {
public String store(String name);
}
The webapp is showing but the variable service is always null. It's not so important to get this code to work, but just if I could get any DI to work. In a distant future I like to persist too, however I would like to do it manually instead of getting a huge working app from an architype. My goal is to understand what I'm doing... Any help would be appreciated.
The problem is that the class in which you are trying to inject is a JAX-RS resource.
JAX-RS resources are a bit of an oversight in Java EE where it concerns the alignment of managed bean types. When Java EE 6 was created it just happened to be that JAX-RS (and JSF 2) finished early, while CDI and the overarching "managed bean" concept finished late.
JAX-RS is a container managed type of bean, but unfortunately not of the official "managed bean" variety, and it thus does not support #EJB directly.
You can make it a CDI managed bean and then use #Inject instead of #EJB.
If you want to inject a reference to your LinkService EJB into the LinkResource resource, then your LinkResource must be a managed component, in other words a stateless session bean. If you add a #Stateless annotation in your LinkResource class, you should be fine.
Context and Dependency injection needs to be enabled per project. In netbeans right click your project and choose New > Other
Under categories select Contexts and Dependency injection then select beans.xml under File types.
Then click Next and then Finish.
Or you can manually create the beans.xml file in Web Pages/WEB-INF folder. Contents should be:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>

Resources