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.
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.
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!!
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() {
// ...
}
}
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).