Sniffy is a cool little project:
Sniffy counts the number of executed SQL queries and provides an API for validating them It is designed for unit tests and allows you to test if particular method doesn't make more than N SQL queries Especially it's useful to catch the ORM N+1 problem at early stages
It also provides a servlet filter which injects HTML into a page with a popup showing you executed queries. The documentation explains how to configure it for a traditional web.xml based application but not Spring Boot. I managed to register the servlet filter by adding this bean to an #Configuration class:
#Bean
public FilterRegistrationBean snifferFilter()
{
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
SnifferFilter filter = new SnifferFilter();
filter.setInjectHtml(true);
filterRegistrationBean.setFilter(filter);
filterRegistrationBean.setName("sniffer");
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
I also updated the JDBC url, the documentation says:
add sniffer: prefix to the JDBC connection url For example jdbc:h2:~/test should be changed to sniffer:jdbc:h2:mem:
So I added the following to my application.yml:
spring.datasource.url: sniffer:jdbc:mysql://localhost:3306
But when I start my application it fails with this error:
URL must start with 'jdbc'
Sniffy author here!
Indeed as of version 3.0.7 (April 2016) you have to specify the driver class name explicitly in your Spring Boot application. There's an open issue in a bug tracker to configure it automatically.
By the way sniffy 3.0.5 introduced an out-of-the box support of Spring Boot using #EnableSniffy annotation, so you do not have to create the FilterRegistrationBean yourself anymore - just put the annotation to your application class like this:
import io.sniffy.boot.EnableSniffy;
#SpringBootApplication
#EnableAutoConfiguration
#EnableSniffy
public class Application {
public static void main(String[] args) throws ClassNotFoundException {
SpringApplication.run(Application.class, args);
}
}
I managed to figure out the problem, Spring Boot makes extensive use of auto configuration and was trying to detect the DatabaseDriver from the connection string. As the connection string no longer starts with jdbc it was encountering a problem.
It was simply a case of specifying the driver-class-name in my application.yml rather than letting Spring Boot trying to auto detect it:
spring.datasource.driver-class-name: io.sniffy.MockDriver
Related
I'm new in Spring applications, and see the big difference between configurations in springBoot and spring. So my questin is: apart from spring-boot, is there a way to setup a proper spring application(with web mvc, security, aop, ...), without any xml config file (ie : config relying only on annotations).
Yes, there is a way to do this in Spring. Spring Boot is after all an enhanced, autoconfigured Spring (with other cool features). That means that everything there is in Spring Boot should be achievable in Spring as well, but you would have do a bit/a lot of Your own extra work.
Moving straight to the point, in order to achieve what you want, you would need to undertake the following steps:
Create a class, which will store all the configuration (basically the properties you would store in the xml file) - let's call it AppConfig.class
Annotate the AppConfig.class with #Configuration - this will inform Spring that this class is the source of configuration;
Annotate the AppConfig.class with #ComponentScan("com.app") - here, You need to provide a package, from which Spring has to start component scanning in order to find Beans to be registered in Spring Container. Important note is, that it will scan the package and it's subpackages, so you would mostly want to provide here the top level package;
If you need some data to be injected into your beans, you would want to use the #PropertySource("classpath:application.properties") - I have provided here the default value, which Spring Boot uses internally in case you want to inject some data into your beans at runtime. For this to work, you need to inject into AppConfig.class an Environment.class
To show it on the example:
#Configuration
#ComponentScan("com.app")
#PropertySource("classpath:application.properties")
public class AppConfig {
// it will help to pull the properties incorporated in the file you have provided in the #PropertySource annotation
private Environment environment;
//inject it
public AppConfig(Environment environment) {
this.environment = environment;
}
// build your beans - the getProperty method accepts the key from application.properties
// file and return a value as a String. You can provide additional arguments to convert
//the value and a default value if the property is not found
#Bean
public Product product() {
return new Product(
environment.getProperty("product.name", "XXX"),
environment.getProperty("product.price", BigDecimal.class, BigDecimal.ZERO),
environment.getProperty("product.quantity", Integer.class, 10)
);
}
}
I hope that it helps
With introduction of 5th version, Spring has changed the default URL pattern matching mechanism from AntPathMatcher to PathPattern class. Spring Boot 2.1, which is based on Spring 5.1 version, does not follow this chage, as AntPathMatcher it still used for the processing:
#GetMapping("/spring5/{*id}") //PathPattern implementation, compilation error
#GetMapping("/spring5/**") // AntPathMatcher implementation, works fine
Is there a way to enable PathPattern matching mechanisms for Spring Boot 2.1 applications?
Ant Matcher will work with
/spring5/**
and normal path pattern will work with
#GetMapping("/spring5/{*id}")
public void methodName(#PathVariable String id)
try and add #PathVariable as well and it will work for you.
I am using Spring mvc, and i found out this solution for Spring mvc:
In your webApplicationcontext configuration file, override the "configurePathMatch" method as follows:
#Configuration
public class WebConfig implements WebMvcConfigurationSupport {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setPatternParser(new PathPatternParser());
}
As Spring mvc official document states:
"setPatternParser(PathPatternParser patternParser): Enable use of parsed PathPatterns as described in AbstractHandlerMapping.setPatternParser(PathPatternParser)."
Hope this will help you somehow!
I'm using hoverfly in my spring boot project's unit test.
The background
The spring boot project will grab its config (connection timeout etc.) from spring cloud config server.
To test whether my timeout configs work, I write a unit test, and expect the hoverfly can return with a long delay , then my customized restTemplate can throw timeout error instead of wait.
The unit test looks lilke this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = TestApplication.class)
#FixMethodOrder(value = MethodSorters.NAME_ASCENDING)
public class CustomRestTemplateTest {
#Autowired
private RestTemplate customRestTemplate;
#ClassRule
public static HoverflyRule hoverflyRule = HoverflyRule.inSimulationMode(SimulationSource.dsl(
service("www.test.com")
.get("/")
.willReturn(success(HttpBodyConverter.json("{}")).withDelay(10, TimeUnit.SECONDS))
));
#Test
public void connectionTimeoutTest() {
customRestTemplate.getForObject("www.test.com", Object.class);
}
}
The issue
As I mentioned in section The background, when my spring boot project starts, it will grab configs from spring cloud config server, but Hoverfly captured that request and try to find the corresponding record, of course it can't , because I only defined the records for my unit test(e.g. www.test.com), so it throws error:
{"destination":"172.16.2.84:8888","error":"No match found","key":"a7ac72c9bcc3dc2b76bf0877d98f9e3a","level":"warning","method":"GET","msg":"Failed to find matching request template from template store","path":"************","query":"","time":"2017-03-08T20:55:28+08:00"}
How could I fix this? I want use hoverfly, can I set some config and exclude config server's url?
Hoverfly's developer Tommy responded me in their email list
It's a known issue: https://github.com/SpectoLabs/hoverfly-java/issues/19
Update
This has been fixed by Tommy Situ, and the code fix will be release in v0.4.3
I use spring-boot-starter-data-solr and would like to make use of the schmea cration support of Spring Data Solr, as stated in the documentation:
Automatic schema population will inspect your domain types whenever the applications context is refreshed and populate new fields to your index based on the properties configuration. This requires solr to run in Schemaless Mode.
However, I am not able to achieve this. As far as I can see, the Spring Boot starter does not enable the schemaCreationSupport flag on the #EnableSolrRepositories annotation. So what I tried is the following:
#SpringBootApplication
#EnableSolrRepositories(schemaCreationSupport = true)
public class MyApplication {
#Bean
public SolrOperations solrTemplate(SolrClient solr) {
return new SolrTemplate(solr);
}
}
But looking in Wireshark I cannot see any calls to the Solr Schema API when saving new entities through the repository.
Is this intended to work, or what am I missing? I am using Solr 6.2.0 with Spring Boot 1.4.1.
I've run into the same problem. After some debugging, I've found the root cause why the schema creation (or update) is not happening at all:
By using the #EnableSolrRepositories annotation, an Spring extension will add a factory-bean to the context that creates the SolrTemplate that is used in the repositories. This template initialises a SolrPersistentEntitySchemaCreator, which should do the creation/update.
public void afterPropertiesSet() {
if (this.mappingContext == null) {
this.mappingContext = new SimpleSolrMappingContext(
new SolrPersistentEntitySchemaCreator(this.solrClientFactory)
.enable(this.schemaCreationFeatures));
}
// ...
}
Problem is that the flag schemaCreationFeatures (which enables the creator) is set after the factory calls the afterPropertiesSet(), so it's impossible for the creator to do it's work.
I'll create an issue in the spring-data-solr issue tracker. Don't see any workaround right now, other either having a custom fork/build of spring-data or extend a bunch of spring-classes and trying to get the flag set before by using (but doubt of this can be done).
Is there a way to run Flyway Java-based callbacks with Spring boot?
I'm converting an existing project that after each migration updates some view definitions, and this is done by Java as it needs some extra logic. I know it could be done in pl/pgsql (we are using Postgres) but it is already done and tested in Java.
Spring boot docs says it is possible, but it is listed that the callback scripts should live in same dir as migrations, maybe this works just for SQL based callbacks.
This code works without Spring Boot:
Flyway flyway = new Flyway();
flyway.setDataSource(this.getDataSource());
flyway.setLocations("/db/migration");
flyway.setCallbacks(new LogMaintenanceFlywayCallback());
flyway.migrate();
I have several migrations in /db/migration and after each one I need to execute my callback. It works in my current project and I need to do the same (or another way to get the same behavior) in Spring Boot.
You can have a configuration like this and it will work:
#Configuration
public class FlywayFactory {
#Bean
public FlywayMigrationInitializer flywayInitializer(Flyway flyway, FlywayCallback flywayCallback) {
flyway.setCallbacks(flywayCallback);
return new FlywayMigrationInitializer(flyway);
}
#Bean
public FlywayCallback flywayCallback() {
return new LogMaintenanceFlywayCallback();
}
}
Since method setCallbacks(Callback... callbacks) of the Flyway has been deprecated and will be removed in Flyway 6.0, you can use new API and FlywayConfigurationCustomizer to set up custom Java-based callbacks. Then the configuration is as below:
#Configuration
public class FlywayFactory {
#Bean
public FlywayConfigurationCustomizer flywayConfigurationCustomizer() {
return configuration -> configuration.callbacks(new LogMaintenanceFlywayCallback());
}
}
There seems to be no possibility to set the callbacks in the Spring Boot autoconfiguration (See FlywayAutoConfiguration.java)
There are 2 things you can do:
Create your own Flyway instance in one of your Configuration classes. Spring Boot will not create his instance in case you do that.
Autowire the Flyway instance in one of your Configuration classes and call the setCallbacks method in a PostConstruct method (But it might be tricky to make sure you call the setter before the migration starts)
You can override the Flyway migration stragtey
#Component
public class CallbackFlywayMigrationStrategy implements FlywayMigrationStrategy {
#Override
public void migrate(Flyway flyway) {
flyway.setCallbacks(new LogMaintenanceFlywayCallback());
flyway.migrate();
}
}
You can define a bean of type org.flywaydb.core.api.callback.Callback as follows:
#Bean
public Callback logMaintenanceFlywayCallback() {
return new LogMaintenanceFlywayCallback();
}