Can't access API endpoint - java

So I've started learning Spring recently by following a small course where we created a market api, when we started we created a simple hello world endpoint to test things out. Recently we just created an endpoint for accessing a list of products, but it seems all requests return a 404 error, since this error seems to be related with the controller I don't think posting all my code is needed.
This is my controller ProductController.java, I've only added the mapping for the first two methods (since I'm trying to fix this error still)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/products")
public class ProductController {
#Autowired
private ProductService productService;
#GetMapping("/all")
public List<Product> getAll() {
return productService.getAll();
}
#GetMapping("/{productId}")
public Optional<Product> getProduct(#PathVariable("productId") int productId) {
return productService.getProduct(productId);
}
public Optional<List<Product>> getByCategory(int categoryId) {
return productService.getByCategory(categoryId);
}
public Product save(Product product) {
return productService.save(product);
}
public Boolean delete(int productId) {
return productService.delete(productId);
}
}
I also had to deal with a bean not being found which translated domain objects into dto's (and vise versa) by using MapStruct with the following error:
I made sure to annotate my interface with #Mapper(componentModel="spring")
***************************
APPLICATION FAILED TO START
***************************
Description:
Field mapper in com.platzi.market.persistance.ProductoRepository required a bean of type 'com.platzi.market.persistance.mapper.ProductMapper' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.platzi.market.persistance.mapper.ProductMapper' in your configuration.
I managed to fix this with this (source from another student's comment)
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication(scanBasePackages = { "com.platzi.market.persistance.mapper.ProductMapper", })
public class PlatziMarketApplication {
public static void main(String[] args) {
SpringApplication.run(PlatziMarketApplication.class, args);
}
}
But I'm not sure if this could be causing some interference with the controller class.
Are you accessing the correct endpoint?
This is my application.properties:
spring.profiles.active=dev
server.servlet.context-path=/platzi-market/api
And this is the active dev profile (application-dev.properties)
server.port=8080
# Database, values are altered
spring.datasource.url=jdbc:mysql://localhost:3306/platzi-market
spring.datasource.username=foo
spring.datasource.password=bar
So the endpoint for accesing all product in my controller should be: localhost:8080/platzi-market/api/products/all which is returning a 404
I also checked if maybe I am using https so I made sure to use http:// in Postman, which also returned a 404
I double-checked the output in the terminal just to make sure the right port and context-path are being used:
2021-02-23 17:20:07.583 INFO 51334 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '/platzi-market/api'
2021-02-23 17:20:07.594 INFO 51334 --- [ main] c.platzi.market.PlatziMarketApplication : Started PlatziMarketApplication in 3.881 seconds (JVM running for 4.296)
If you would like to check the rest of the code here's a link to the repo: https://github.com/Je12emy/spring-market-api, hope this makes since since I'm going crazy with error XD

I could reproduce your problem... (with Eclipse STS 4.9.0).
When running as "Spring boot run configuration" (in eclipse) the context comes up, but with a decent warning. I can reproduce the 404 error pages (due to missing /error mapping!).
I removed the (useless)scanBasePackages, and found the context not starting. (with the described problems).
To build.gradle, I added:
plugins {
...
id "com.diffplug.eclipse.apt" version "3.26.0" // Only for Eclipse
}
..as described by https://mapstruct.org/documentation/stable/reference/html/#_gradle
Then I (recognized, that no mapperImpls were generated,) tried to run via gradle :bootRun and found following generation/"compilation" errors in your mappers:
In CategoryMapper it must be "description" not "descripcion".
Missing Getter/Setter for productos in Categoria.
Missing Getter/Setter for categoria in Producto.
Applying these fixes, (& running (at least initially/every time, when mapper/dto changes) with gradle), should* bring up the application and give access to loclahost:8080/platzi-market/api/products/all.
*: I "tested" on an empty (different/h2/in-memory) database. (It means, there can be runtime bugs.)
A warm welcome to a spring!
Reviewing your repo, I can recommend you:
(next) to create some tests (for mappers, repos, controllers ... code-coverage)
probably Spring Boot - Loading Initial Data.
and of course don't: expose (real) prod-credentials in public repos. ;)
...

Related

Debugging SpringBoot MVC service application 404 error

A web application I've been working on recently the past like 2 weeks maybe for whatever reason when I finally tested it - won't seem to even enter the method that I have to return a JSON list of objects. I have included the Jackson library and Spring Boot Web, Tomcat, Data-JPA, Hibernate, MySQL, and a library to allow me to access JSP files. The index.jsp comes up but I almost feel like Spring Boot is giving me that free of charge as it's not even entering that method. I have been having the issue for a few days but trying to resolve it on my own - I found another answer that suggested to put a breakpoint inside one of the Spring classes but when I "debugged" it through Eclipse, it didn't even stop at that class - something about pattern matching - One answer suggested adding a context to the application.properties file - didn't help. I've reduced it to as simple as I think I can get it. Can anyone tell me what I might be doing wrong? Before my code, the project is on Github at: https://github.com/sfulmer/Scheduler.git
Here's my controller:
package net.draconia.schedule.controllers;
import java.util.List;
import net.draconia.schedule.beans.Event;
import net.draconia.schedule.dao.EventDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
public class ScheduleController
{
private static final Logger logger = LoggerFactory.getLogger(ScheduleController.class);
#Autowired
private EventDAO mObjDAO;
protected EventDAO getDAO()
{
return(mObjDAO);
}
//#GetMapping("/events")
#RequestMapping(value = "events", method = RequestMethod.GET)
public #ResponseBody List<Event> getEvents()
{
logger.debug("I got here");
return(getDAO().getList());
}
#GetMapping("/")
public String index()
{
return("index");
}
}
Here is the DAO interface - I'll show the class if necessary but this is what the controller looks at:
package net.draconia.schedule.dao;
import java.util.List;
import javax.persistence.EntityNotFoundException;
import net.draconia.schedule.beans.Event;
public interface EventDAO
{
public Event getEventById(final long lId) throws EntityNotFoundException;
public List<Event> getList();
public void remove(final Event objEvent);
public void removeById(final long lId);
public Event save(final Event objEvent);
}
The Event class is so long but if I need to include it, I will. The application.properties file is here:
spring.datasource.url = jdbc:mysql://localhost:3306/schedule
spring.datasource.username = root
spring.datasource.password = R3g1n# M1lL$ 1$ My Qu3eN!
spring.mvc.view.prefix: /WEB-INF/jsp/
spring.mvc.view.suffix: .jsp
server.servlet.contextPath=/scheduler
and here is my Application class(with the SpringBootApplication annotation):
package net.draconia.schedule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#SpringBootApplication(scanBasePackages = {"net.draconia.schedule.controller"})
public class ScheduleApp implements WebMvcConfigurer
{
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder)
{
return(builder.sources(ScheduleApp.class));
}
public static void main(String[] args)
{
SpringApplication.run(ScheduleApp.class, args);
}
}
I'm relatively new to Spring Boot but haven't ever ran into this problem ever before as I work with it at work and it works fine but we use entirely REST services there and I am using JSP files as well as sorta end-points that respond with JSON but you can't respond from REST services with JSP views so unfortunately I can't copy work's project to get that working or I would sigh Any thoughts on how I can get this working or what I am omitting?
My guess is that you're mixing things from Spring and Spring boot, and that's getting problems on loading beans, as you're probably changing the annotations load order or loading other beans rather than spring boot defaults as expected. For example, you implements WebMvcConfigurer, but you aren't providing any WebMvc Configuration, like a ViewResolver bean
My advice is to follow this guide: https://spring.io/guides/gs/spring-boot/
and use only the annotations from spring boot if using spring boot, or spring if using spring (they're similar, but not exactly the same, configuration is different).
Anyways, you can check loaded beans in Spring application context (Inject it in Application class) with ctx.getBeanDefinitionNames() method and see if your controller is there (i guess not).
By looking into code, my first impression is that, you have some typo in here:
#SpringBootApplication(scanBasePackages = {"net.draconia.schedule.controller"})
Your controller class package name has net.draconia.schedule.controllers.
So can you please correct your scanBasePackages with proper package name.
If that is not the case, please update full stack trace along with GET request which you are submitting into application. Will take a look & update answer accordingly.

Spring Boot 2 Actuator doesnt publish jvm metric

I am running a Spring Boot 2 Application and added the actuator spring boot starter dependency. I enabled all web endpoints and then called:
http://localhost:8080/actuator/metrics
result is:
{
"names": ["jdbc.connections.active",
"jdbc.connections.max",
"jdbc.connections.min",
"hikaricp.connections.idle",
"hikaricp.connections.pending",
"hikaricp.connections",
"hikaricp.connections.active",
"hikaricp.connections.creation",
"hikaricp.connections.max",
"hikaricp.connections.min",
"hikaricp.connections.usage",
"hikaricp.connections.timeout",
"hikaricp.connections.acquire"]
}
But I am missing all the JVM stats and other built-in metrics. What am I missing here? Everything I read said that these metrics should be available at all times.
Thanks for any hints.
I want to share the findings with you. The problem was that a 3rd party library (Shiro) and my configuration for it. The bean loading of micrometer got mixed up which resulted in a too late initialisation of a needed PostProcessingBean which configures the MicroMeterRegistry (in my case the PrometheusMeterRegistry).
I dont know if its wise to do the configuration of the Registries via a different Bean (PostProcessor) which can lead to situations i had... the Registries should configure themselves without relying on other Beans which might get constructed too late.
In case this ever happens to anybody else:
I had a similar issue (except it wasn't Graphite but Prometheus, and I was not using Shiro).
Basically I only had Hikari and HTTP metrics, nothing else (no JVM metrics like GC).
I banged my head on several walls before finding out the root cause: there was a Hikari auto configure post processor in Spring Boot Autoconfigure that eagerly retrieved a MeterRegistry, so all Metric beans didn't have time to initialize before.
And to my surprise, when looking at this code in Github I didn't find it. So I bumped my spring-boot-starter-parent version from 2.0.4.RELEASE to 2.1.0.RELEASE and now everything works fine. I correctly get all the metrics.
As I expected, this problem is caused by the loading order of the beans.
I used Shiro in the project.
Shiro's verification method used MyBatis to read data from the database.
I used #Autowired for MyBatis' Mapper file, which caused the Actuator metrics related beans to not be assembled by SpringBoot (I don't know what the specific reason is).
So i disabled the automatic assembly of the Mapper file by manual assembly.
The code is as follows:
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
SpringContextUtil.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static Object getBean(String beanId) throws BeansException {
return applicationContext.getBean(beanId);
}
}
Then
StoreMapper userMapper = (UserMapper) SpringContextUtil.getBean("userMapper");
UserModel userModel = userMapper.findUserByName(name);
The problem can be solved for the time being. This is just a stopgap measure, but at the moment I have no better way.
I can not found process_update_seconds in /actuator/prometheus, so I have spent some time to solve my problem.
My solution:
Rewrite HikariDataSourceMetricsPostProcessor and MeterRegistryPostProcessor;
The ordered of HikariDataSourceMetricsPostProcessor is Ordered.HIGHEST_PRECEDENCE + 1;
package org.springframework.boot.actuate.autoconfigure.metrics.jdbc;
...
class HikariDataSourceMetricsPostProcessor implements BeanPostProcessor, Ordered {
...
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 1;
}
}
The ordered of MeterRegistryPostProcessor is Ordered.HIGHEST_PRECEDENCE;
package org.springframework.boot.actuate.autoconfigure.metrics;
...
import org.springframework.core.Ordered;
class MeterRegistryPostProcessor implements BeanPostProcessor, Ordered {
...
#Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
In my case I have used shiro and using jpa to save user session id. I found the order of MeterRegistryPostProcessor and HikariDataSourceMetricsPostProcessor cause the problem. MeterRegistry did not bind the metirc because of the loading order.
Maybe my solution will help you to solve the problem.
I have a working sample with Spring Boot, Micrometer, and Graphite and confirmed the out-of-the-box MeterBinders are working as follows:
{
"names" : [ "jvm.memory.max", "process.files.max", "jvm.gc.memory.promoted", "tomcat.cache.hit", "system.load.average.1m", "tomcat.cache.access", "jvm.memory.used", "jvm.gc.max.data.size", "jvm.gc.pause", "jvm.memory.committed", "system.cpu.count", "logback.events", "tomcat.global.sent", "jvm.buffer.memory.used", "tomcat.sessions.created", "jvm.threads.daemon", "system.cpu.usage", "jvm.gc.memory.allocated", "tomcat.global.request.max", "tomcat.global.request", "tomcat.sessions.expired", "jvm.threads.live", "jvm.threads.peak", "tomcat.global.received", "process.uptime", "tomcat.sessions.rejected", "process.cpu.usage", "tomcat.threads.config.max", "jvm.classes.loaded", "jvm.classes.unloaded", "tomcat.global.error", "tomcat.sessions.active.current", "tomcat.sessions.alive.max", "jvm.gc.live.data.size", "tomcat.servlet.request.max", "tomcat.threads.current", "tomcat.servlet.request", "process.files.open", "jvm.buffer.count", "jvm.buffer.total.capacity", "tomcat.sessions.active.max", "tomcat.threads.busy", "my.counter", "process.start.time", "tomcat.servlet.error" ]
}
Note that the sample on the graphite branch, not the master branch.
If you could break the sample in the way you're seeing, I can take another look.

Spring Boot 2 index.html not loaded automatically from subdirectory mapped as static resource

I have a Maven module containing my Angular 6 application, and at build it is packaged in a jar at META-INF/resources/admin/ui.
My Spring Boot 2 application has a dependency to the frontend Maven module and when building it includes the frontend library as well. However, if I access http://localhost:8080/admin/ui/ it downloads an empty ui file, but if I access http://localhost:8080/admin/ui/index.html then it displays the Angular application.
If I package the frontend application at META-INF/resources/ then http://localhost:8080/ will display the Angular application correctly, but I want the context of the frontend application to start from /admin/ui. The Spring Boot application does not have any custom mappings, it is just annotated with
#Configuration
#EnableAutoConfiguration
#EnableScheduling
#ComponentScan(basePackageClasses = {...})
#Import({...})
Is there a configuration property that I am missing?
I appreciate the help.
You don't need all those annotations to make it working... I would recommend please remove those which are not added purposely by you..!!
To serve your static page on different path than the main context, here is a work-around..!!
Create another simple controller class like below..
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class Home {
#RequestMapping(path = "/")
public String getHome(){
return "redirect:/admin/ui/";
// make sure no space between colon (:) and endpoint name (/admin/ui)
}
#RequestMapping(path = "/admin/ui/" )
public String getAdminUi(){
return "/index.html";
// your index.html built by angular should be in resources/static folder
// if it is in resources/static/dist/index.html,
// change the return statement to "/dist/index.html"
}
}
And, notice here, I have marked the class as #Controller not the #RestController so if you mark it to #RestController or try to do the same in any existing #RestController you would not achieve it easily. So, it's no harm to create another class like above.
Benefit of this way is, it don't destroy your existing mappings.. also the context path is not changes, so no need to bother about your other endpoint paths. They all shall work as before.
Hope this helped!!

Spring Boot Test does not use jmx property

I have a test clas with
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class Foo{
...
}
which should start up a regular application context as defined by:
#SpringBootApplication(scanBasePackages = {"de.foo", "de.bar"})
public class Application {
...
}
This works as expected. Further I have an application.yml which gets loaded in both cases but when running the test, the property for JMX (spring.jmx.enabled) does not get loaded or it does not get used.
I tried different property files (application.yml, application-test.yml) but the only thing what works is setting the property via
#TestPropertySource(properties = "spring.jmx.enabled:true")
The property defaults to true in a regular application context.
Several questions:
Why is the default different in a test class?
Why does the property not get loaded or recognized, when loading it from an application.yml (the rest of the yml works, so it does get loaded).
This seems to be a known behavior, as seen in this comment in Spring Boot Sample Data Tests. Is there any documentation I missed about this behavior?
I've recently encountered the same situation myself, and have opened spring-projects/spring-boot#13008 to document this behavior. As a result, the following additions to the reference manual will be added in the upcoming 1.5.13.RELEASE and 2.0.2.RELEASE:
As the test context framework caches context, JMX is disabled by default to prevent identical components to register on the same domain. If such test needs access to an MBeanServer, consider marking it dirty as well:
#RunWith(SpringRunner.class)
#SpringBootTest(properties = "spring.jmx.enabled=true")
#DirtiesContext
public class SampleJmxTests {
#Autowired
private MBeanServer mBeanServer;
#Test
public void exampleTest() {
// ...
}
}

How to run Spring integation tests when using aspects?

I'm developing a Spring MVC web application (Java 6 and Spring 3.0.6 currently).
I'm starting to write some Spring integration tests using Junit4 that extend AbstractTransactionalJUnit4SpringContextTests. I invoke these either through our Maven build or in the EclipseIDE (3.7). These tests invoke Controller methods (i.e., methods annotated with #RequestHandler in a class annotated with #Controller).
All was going well until I added aspect-based logging into the controller :
// public controller methods
#Pointcut("execution(public * com.axiope.webapp.controller.*.*(..))")
private void publicControllerMethod() {
}
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
private void requestHandler(){}
#Pointcut("publicControllerMethod() && requestHandler() ")
private void controllerHandler(){}
// logs contoller exceptions
#AfterThrowing(
pointcut="controllerHandler()",
throwing="ex")
public void logControllerExceptions(Throwable ex) {
logger = LogFactory.getLog(ex.getClass());
logger.error("Controller exception !" + ex.getMessage());
}
Now when I run tests through Maven I get an error like :
No unique bean of type [com.axiope.webapp.controller.StructuredDocumentController]
is defined: expected single bean but found 0:
In the tests, I'm loading the controller from the applicationContext in the setUp method:
structuredDocumentController = applicationContext.getBean(
StructuredDocumentController.class);
This error doesn't happen if I comment out the aspect. I suspect it has something to do with Spring proxying the controller and then the controller class isn't identifiable by its class name. I've tried declaring the controller as a bean in applicationContext.xml but this doesn't help. This problem also occurs when running the tests in Eclipse, so it's not a problem with my Maven configuration.
My question is : how can I get the controller bean detected in the tests?
Would be really grateful for any help - is it wrong to add aspects to methods in controller classes? Should I disable aspects somehow when testing? (Although ideally I'd like to see in the integration tests that logging is working properly).
Thanks very much
Richard
Aspects are not the likely issue, you should be able to use them in the controller also without any problems.
My guess is that you are not loading up the correct context for your tests - how have you specified the application context to use for this test - is there #ContextConfiguration on your test class with the location, is the location to Root context(one specified through ContextLoaderListener) or Web application Context(one specified via DispatcherServlet).

Categories

Resources