How to make springboot register a RestController? - spring-boot

I havent worked too much with Spring API tools before, have more experience with Jersey. I have looked at many different articles explaining how to get REST endpoints wired up with Springboot and I just dont see why this is not working.
Here are some relevant code snippets:
In application.properties
server.port=5000
The package is com.myproj
#SpringBootApplication(scanBasePackages={"com.myproj"})
public class ClaimsIntakeServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ClaimsIntakeServiceApplication.class, args);
}
}
In com.myproj.controllers
#Controller
#RequestMapping(value = "/hello")
public class ClaimController {
#GetMapping
public ResponseEntity<Item> read() {
System.out.println("hi");
return new ResponseEntity<>("", HttpStatus.OK);
}
If i hit this URL in postman: https://localhost:5000/hello ..
It says
connection refused
How do I wire up a rest endpoint with springboot so I can hit it with postman as shown above?

Something like this should work:
Main class:
#SpringBootApplication
#ComponentScan(basePackages = {"com.myproj"})
public class HelloWorldApplication {
public static void main(String[] args) {
SpringApplication.run(HelloWorldApplication.class, args);
}
}
Controller:
#RestController
public class HelloWorldController {
#RequestMapping(path = "/hello-world", method = RequestMethod.GET, produces =
MediaType.APPLICATION_JSON_VALUE)
public HelloWorldResponse getHelloWorld() {
return new HelloWorldResponse("hello!!");
}
}
Docs here

For some reason while i get all the stuff for API with this:
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-cosmosdb-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
I had to include this as well in order for the API to work:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

Related

Spring Boot - connect resource server to Spring Session with an Redis server

For evaluation purposes we would like to use Spring Session in combination with an Embedded-Redis server.
When using Spring Boot 2.6.7 with JDK17 we start the Spring Session with an Embedded-Redis server. The server is started just after the 'UI' server. The code and config is given at the end.
The 'resource' server that is receiving the Spring Session token cannot connect to redis.
The resource server code is:
#SpringBootApplication
#RestController
public class ResourceApplication extends WebSecurityConfigurerAdapter {
private final static Logger logger = LoggerFactory.getLogger(ResourceApplication.class);
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().authorizeRequests().anyRequest().authenticated();
}
#RequestMapping("/")
#CrossOrigin(origins = "*", maxAge = 3600, allowedHeaders = { "x-auth-token", "x-requested-with", "x-xsrf-token" })
public Message home() {
logger.info( "ResourceServer: home()");
return new Message("Hello World");
}
#Bean
HeaderHttpSessionStrategy sessionStrategy() {
return new HeaderHttpSessionStrategy();
}
public static void main(String[] args) {
SpringApplication.run(ResourceApplication.class, args);
}
}
The properties are:
security.sessions=NEVER
spring.session.store-type=redis
spring.redis.host=localhost
spring.redis.port=6379
The dependencies are:
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
When adding e.g. the spring-session-data-redis
The error message is:
An attempt was made to call a method that does not exist. The attempt
was made from the following location:
org.springframework.boot.autoconfigure.session.RedisSessionConfiguration$SpringBootRedisHttpSessionConfiguration.customize(RedisSessionConfiguration.java:80)
When adding the spring-sessions-data-redis dependency, gives the same error message.
Only for your information, starting up the Redis-embedded server goes like this:
#SpringBootApplication
#EnableWebSecurity
public class DemoApplication {
private Logger logger = LoggerFactory.getLogger(getClass());
private RedisServer redisServer;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#PostConstruct
public void startRedis() throws IOException {
try {
redisServer = RedisServer.builder().setting("maxheap 256Mb").port(6379).build();
redisServer.start();
} catch (Exception e) {
e.printStackTrace();
}
}
#PreDestroy
public void stopRedis(){
redisServer.stop();
}
}
Dependencies:
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
<dependency>
<groupId>it.ozimov</groupId>
<artifactId>embedded-redis</artifactId>
<version>0.7.3</version>
</dependency>
The resources:
spring.redis.host=localhost
spring.redis.password=
spring.redis.port=6379
spring.session.store-type=redis

Spring boot controller not being detected

Good day. I'm having issues with my Springboot app. The controller isn't being detected.The Component scan is used but it won't detect the controllers.
Folder structure
Application
#EntityScan(basePackages = "com.jokedata.models")
#EnableJpaRepositories(basePackages = "com.jokedata.repositories")
//#ComponentScan(basePackages = {"com.jokeweb.project.controllers"})
#SpringBootApplication(scanBasePackages = "com.jokeweb.project.controllers")
public class JokeApplication {
public static void main(String[] args) {
SpringApplication.run(JokeDataApplication.class, args);
}
}
Controller
#RestController
public class UserController {
#GetMapping("/home")
public String home() {
return "home";
}
}
Check if request URL or application port is correct or not.

Spring Aspect doesn't work if bean is instantiated manually first

I have a simple aspect like below
#Aspect
public class PersistentAspect {
#AfterReturning("#annotation(org.aspect.PersistentOperation)")
public void log(JoinPoint jp) {
System.out.println("aspect call");
}
}
and an AppConfig like below
public class AppConfig {
private Integer num;
private String text;
public Integer getNum() {
return num;
}
#PersistentOperation
public void setNum(Integer num) {
this.num = num;
}
public String getText() {
return text;
}
#PersistentOperation
public void setText(String text) {
this.text = text;
}
}
And configuration class like below
#EnableWs
#Configuration
public class WsConfig extends WsConfigurerAdapter {
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
AppConfig config = config();
interceptors.add(new CustomValidatingInterceptor(schema(), null));
}
#Bean
public AppConfig config() {
AppConfig config = null;
config = new AppConfig();
return config;
}
#Bean
public PersistentAspect persistentAspect() {
PersistentAspect persistentAspect = new PersistentAspect();
return persistentAspect;
}
}
If I use below in the addInterceptors
AppConfig config = config();
The Aspect will not work. The obvious solution I have is to change the code to
AppConfig config = new AppConfig();
Now what I want to understand is, is there a config in which AppConfig config = config(); could still be made working. I assume that when spring initiates the Bean, it can make an AOP proxy of the AppConfig, and when I initiate the bean it interferes with that process somehow. What is the spring/spring-boot way of handling this?
Below is the pom.xml, so basically latest Spring 5.0.5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Edit-1
Before posting the I had already adding a #EnableAspectJAutoProxy, but that had not helped.
The git repo to try the issue is on below
https://github.com/tarunlalwani/spring-aop-so-50084308
Your AppConfig bean (which you initialize, correctly, using a method annotated with #Bean) needs to be wrapped in a proxy that then executes the aspect logic.
You have to enable this behavior by adding the #EnableAspectJAutoProxy annotation to your WsConfig class.
By the way: it is totally correct to use
AppConfig config = config();
in your addInterceptors method. Spring will return the bean that was created by the config method.

Mocked Spring #Service that has #Retryable annotations on methods fails with UnfinishedVerificationException

I'm using Spring Boot 1.4.0.RELEASE with spring-boot-starter-batch, spring-boot-starter-aop and spring-retry
I have a Spring Integration test that has a #Service which is mocked at runtime. I've noticed that if the #Service class contains any #Retryable annotations on its methods, then it appears to interfere with Mockito.verify(), I get a UnfinishedVerificationException. I presume this must be something to do with spring-aop? If I comment out all #Retryable annotations in the #Service then verify works ok again.
I have created a github project that demonstrates this issue.
It fails in sample.batch.MockBatchTestWithRetryVerificationFailures.batchTest() at validateMockitoUsage();
With something like:
12:05:36.554 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - After test method: context [DefaultTestContext#5ec0a365 testClass = MockBatchTestWithRetryVerificationFailures, testInstance = sample.batch.MockBatchTestWithRetryVerificationFailures#5abca1e0, testMethod = batchTest#MockBatchTestWithRetryVerificationFailures, testException = org.mockito.exceptions.misusing.UnfinishedVerificationException:
Missing method call for verify(mock) here:
-> at sample.batch.service.MyRetryService$$FastClassBySpringCGLIB$$7573ce2a.invoke(<generated>)
Example of correct verification:
verify(mock).doSomething()
However I have another class (sample.batch.MockBatchTestWithNoRetryWorking.batchTest()) with a mocked #Service that doesn't have any #Retryable annotation and verify works fine.
What am I doing wrong?
In my pom.xml I have the following:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
</parent>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
...
Then all the related Java Classes
#SpringBootApplication
#EnableBatchProcessing
#Configuration
#EnableRetry
public class SampleBatchApplication {
#Autowired
private JobBuilderFactory jobs;
#Autowired
private StepBuilderFactory steps;
#Autowired
private MyRetryService myRetryService;
#Autowired
private MyServiceNoRetry myServiceNoRetry;
#Bean
protected Tasklet tasklet() {
return new Tasklet() {
#Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext context) {
myServiceNoRetry.process();
myRetryService.process();
return RepeatStatus.FINISHED;
}
};
}
#Bean
public Job job() throws Exception {
return this.jobs.get("job").start(step1()).build();
}
#Bean
protected Step step1() throws Exception {
return this.steps.get("step1").tasklet(tasklet()).build();
}
public static void main(String[] args) throws Exception {
// System.exit is common for Batch applications since the exit code can be used to
// drive a workflow
System.exit(SpringApplication
.exit(SpringApplication.run(SampleBatchApplication.class, args)));
}
#Bean
ResourcelessTransactionManager transactionManager() {
return new ResourcelessTransactionManager();
}
#Bean
public JobRepository getJobRepo() throws Exception {
return new MapJobRepositoryFactoryBean(transactionManager()).getObject();
}
}
#Service
public class MyRetryService {
public static final Logger LOG = LoggerFactory.getLogger(MyRetryService.class);
#Retryable(maxAttempts = 5, include = RuntimeException.class, backoff = #Backoff(delay = 100, multiplier = 2))
public boolean process() {
double random = Math.random();
LOG.info("Running process, random value {}", random);
if (random > 0.2d) {
throw new RuntimeException("Random fail time!");
}
return true;
}
}
#Service
public class MyServiceNoRetry {
public static final Logger LOG = LoggerFactory.getLogger(MyServiceNoRetry.class);
public boolean process() {
LOG.info("Running process that doesn't do retry");
return true;
}
}
#ActiveProfiles("Test")
#ContextConfiguration(classes = {SampleBatchApplication.class, MockBatchTestWithNoRetryWorking.MockedRetryService.class}, loader = AnnotationConfigContextLoader.class)
#RunWith(SpringRunner.class)
public class MockBatchTestWithNoRetryWorking {
#Autowired
MyServiceNoRetry service;
#Test
public void batchTest() {
service.process();
verify(service).process();
validateMockitoUsage();
}
public static class MockedRetryService {
#Bean
#Primary
public MyServiceNoRetry myService() {
return mock(MyServiceNoRetry.class);
}
}
}
#ActiveProfiles("Test")
#ContextConfiguration(classes = { SampleBatchApplication.class,
MockBatchTestWithRetryVerificationFailures.MockedRetryService.class },
loader = AnnotationConfigContextLoader.class)
#RunWith(SpringRunner.class)
public class MockBatchTestWithRetryVerificationFailures {
#Autowired
MyRetryService service;
#Test
public void batchTest() {
service.process();
verify(service).process();
validateMockitoUsage();
}
public static class MockedRetryService {
#Bean
#Primary
public MyRetryService myRetryService() {
return mock(MyRetryService.class);
}
}
}
EDIT: Updated question and code based on a sample project I put together to show the problem.
So after looking at a similar github issue for spring-boot
I found that there is an extra proxy getting in the way. I found a nasty hack by unwrapping the aop class by hand, makes verification work, ie:
#Test
public void batchTest() throws Exception {
service.process();
if (service instanceof Advised) {
service = (MyRetryService) ((Advised) service).getTargetSource().getTarget();
}
verify(service).process();
validateMockitoUsage();
}
Hopefully, this can be fixed similar to the above github issue. I'll raise an issue and see how far I get.
EDIT: Raised the github issue
After I've seen #Joel Pearsons answer, and especially the linked GitHub issue, I worked around this by temporarily using a static helper method that unwraps and verifies:
public static <T> T unwrapAndVerify(T mock, VerificationMode mode) {
return ((T) Mockito.verify(AopTestUtils.getTargetObject(mock), mode));
}
With this method the only difference in the test cases is the verification call. There is no overhead other than this:
unwrapAndVerify(service, times(2)).process();
instead of
verify(service, times(2)).process();
Actually, it was even possible to name the helper method like the actual Mockito method, so that you only need to replace the import, but I didn't like the subsequent confusion.
However, unwrapping shouldn't be required if #MockBean is used instead of mock() to create the mocked bean. Spring Boot 1.4 supports this annotation.

Hystrix Javanica fallback not working in Spring Cloud 1.0

I built a super simple Hystrix short circuit example based on #spencergibb feign-eureka spring cloud starter example. At first I thought I couldn't get the hystrix javanica default fallbackMethod to trigger due to feign.. now, removing feign, the hystrix default fallbackMethod still doesn't catch exceptions.
pom.xml
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>1.0.0.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
:
</dependencies>
Main file:
#SpringBootApplication
#EnableDiscoveryClient
#EnableHystrix
#RestController
public class HelloClientApplication {
#Autowired
HelloClientComponent helloClientComponent;
#RequestMapping("/")
public String hello() {
return helloClientComponent.makeMultipleCalls();
}
public static void main(String[] args) {
SpringApplication.run(HelloClientApplication.class, args);
}
}
HelloClientComponent.java (created because I know javanica expects to be inside a spring managed component or service):
#Component
public class HelloClientComponent {
#Autowired
RestTemplate restTemplate;
public String makeMultipleCalls() {
int cnt=20;
StringBuilder sb = new StringBuilder();
while (cnt-- > 0) {
String response = theServerRequestRoot();
sb.append(response).append(" ");
}
return sb.toString();
}
public String theServersRequestRootFallback() {
System.out.println("BOMB!!!!!!");
return "BOMB!!!!!!";
}
#HystrixCommand(fallbackMethod = "theServersRequestRootFallback", commandKey = "callToServers")
public String theServerRequestRoot() {
ResponseEntity<String> result = restTemplate.getForEntity("http://HelloServer", String.class);
System.out.println(result.getBody());
return result.getBody();
}
}
I start 2 servers, one which always succeeds and responds, and the other will fail 30% of the time with a 500 error. When I curl this client (to '/') things go normally for the non-forced failure calls. Round robining works fine as well. When the second server does return the 500 error, the fallbackMethod does not get called and the curl to '/' ends and returns with an error.
Update with solution per Spencer and Dave's suggestions. Change to the following:
Main Application file:
#SpringBootApplication
#EnableDiscoveryClient
#EnableHystrix
#RestController
public class HelloClientApplication {
#Autowired
HelloClientComponent helloClientComponent;
#RequestMapping("/")
public String hello() {
int cnt=20;
StringBuilder sb = new StringBuilder();
while (cnt-- > 0) {
String response = helloClientComponent.theServerRequestRoot(); // call directly to #Component in order for #HystrixCommand to intercept via AOP
sb.append(response).append(" ");
}
return sb.toString();
}
public static void main(String[] args) {
SpringApplication.run(HelloClientApplication.class, args);
}
}
HelloClientComponent.java:
#Component
public class HelloClientComponent {
#Autowired
RestTemplate restTemplate;
public String theServersRequestRootFallback() {
System.out.println("BOMB!!!!!!");
return "BOMB!!!!!!";
}
#HystrixCommand(fallbackMethod = "theServersRequestRootFallback", commandKey = "callToServers")
public String theServerRequestRoot() {
ResponseEntity<String> result = restTemplate.getForEntity("http://HelloServer", String.class);
System.out.println(result.getBody());
return result.getBody();
}
}
#HystrixCommand only works because Spring makes a proxy to call that method on. If you call the method from within the proxy it doesn't go through the interceptor. You need to call the #HystrixCommand from another #Component (or use AspectJ).

Resources