this is my Spring Boot with Thymeleaf setup.
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
ThymeleafConfig
#Configuration
public class ThymeleafConfig {
#Bean
public ServletContextTemplateResolver templateResolver() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/templates/");
resolver.setSuffix(".html");
resolver.setTemplateMode("LEGACYHTML5");
resolver.setOrder(1);
return resolver;
}
}
Controller
#RestController
public class WebController {
#RequestMapping("")
public String index(){
return "index";
}
}
Index.html is located in src/main/resources/templates.
But when localhost:8080 is called only "index" string is rendered. Index.html is not fetched. What could be the problem?
You're using a #RestController.
All handler methods of a #RestController bean act as if annotated with #ResponseBody, ie. the object they return is written to the response directly based on some HttpMessageConverter.
Change #RestController to #Controller if you don't want that behavior.
Related
I've set up a scenario using caffeine cache and I can't get it working, the real method is always called when the parameters are the same. Here is my config:
pom.xml
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
...
Configuration class for the CacheManager
#Configuration
#EnableCaching
public class CachingConfig {
public static final String CACHE_NAME = "test";
#Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager(CACHE_NAME);
cacheManager.setCaffeine(caffeineConfig());
return cacheManager;
}
private Caffeine caffeineConfig() {
return Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.maximumSize(1024 * 1024 * 256);
}
}
And then the class with the cacheable method:
#CacheConfig(cacheNames = {CachingConfig.CACHE_NAME})
public class MyClass{
#Cacheable
public Object cacheableMethod(String a, String b, Boolean c) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Object()
}
I've tried also adding the cache name to the Cacheable annotation:
#Cacheable(value = CachingConfig.CACHE_NAME)
And moving #EnableCaching to the Spring Boot main application class.
The real method is always been called.
Any ideas of what I'm doing wrong?
Thanks
The #Cacheable method has to be located inside a #Bean, #Component, #Service...
In a Spring Boot 2.1 environment, I would like to use Togglz that are stored in a JDBCStateRepository.
The problem is: The Togglz are not shown in the console. The Togglz are not stored in the database.
My setup happens via the following files:
Maven:
<dependency>
<groupId>org.togglz</groupId>
<artifactId>togglz-spring-boot-starter</artifactId>
<version>2.6.1.Final</version>
</dependency>
<dependency>
<groupId>org.togglz</groupId>
<artifactId>togglz-console</artifactId>
<version>2.6.1.Final</version>
</dependency>
FeatureOptions:
public enum FeatureOptions implements Feature {
#EnabledByDefault
#Label("Zwerfobjecten geheel draaien in volgende snapshot")
FEATURE_ONE;
public boolean isActive() {
return FeatureContext.getFeatureManager().isActive(this);
}
}
TogglzConfiguration:
#Configuration
public class TogglzConfiguration implements TogglzConfig {
#Autowired
private DataSource dataSource;
public Class<? extends Feature> getFeatureClass() {
return FeatureOptions.class;
}
#Bean
public StateRepository getStateRepository() {
return new JDBCStateRepository(dataSource);
}
#Bean
public UserProvider getUserProvider() {
return new NoOpUserProvider();
}
#Bean
public FeatureProvider featureProvider() {
return new EnumBasedFeatureProvider(FeatureOptions.class);
}
}
The application.properties are:
togglz.feature-enums=nl.xyz.project.togglz.FeatureOptions
togglz.console.path=/togglz-console
togglz.console.enabled=true
togglz.console.secured=false
Only one property was missing ...
togglz.console.use-management-port=false
I have few controllers which have the same request path prefix i.e.
/v4/users/{userId}/
And then the methods in the controllers are annotated with separate path mappings. The swagger ui shows only one of those controllers.
#Component
public class BaseController {
}
/******************/
#RestController
#RequestMapping("/v4/users/{userId}/payment")
public class PaymentController extends BaseController {
#RequestMapping(value = "/initiate", method = RequestMethod.POST)
public HttpEntity<Object> initiatePayment() {...}
#RequestMapping(value = "/success", method = RequestMethod.GET)
public HttpEntity<Object> success() {...}
}
#RestController
#RequestMapping("/v4/users/{userId}/preferences")
public class CustomerPreferencesController extends BaseController{
#RequestMapping(value = "/deals/{dealId}", method = RequestMethod.POST)
public HttpEntity<Object> favouriteDeal(...) {...}
//some more methods
}
There is one more controller that has the same path prefix i.e. /v4/users/{userId} which shows up on swagger-ui but not the above two controllers.
pom.xml entries:
<!-- Swagger Spring -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.0.2</version>
</dependency>
UPDATE - 1
#Bean
public Docket mobileAPI() {
return new Docket(DocumentationType.SWAGGER_2)
.host(swaggerHost)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo())
.pathMapping("/");
}
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).
I have the same problem as here and here but couldn't find a solution yet.
So my sample test project will show the whole relevant configuration and code:
Constraint annotation:
#Target({ ElementType.METHOD, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = FooValidator.class)
public #interface FooValid {
String message();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Annotated PoJo:
public class Foo {
#FooValid(message = "Test failed")
private Integer test;
[...]
}
Annotated Service with #Validated:
#Service
#Validated
public class FooService {
private final Test test;
#Autowired
public FooService(final Test test) {
this.test = test;
}
public void foo(#Valid final Foo foo) {
this.test.test(foo);
}
}
JSR-303 ConstraintValidator:
public class FooValidator implements ConstraintValidator<FooValid, Integer> {
#Autowired
private ValidationService validationService;
#Override
public void initialize(final FooValid constraintAnnotation) {
// TODO Auto-generated method stub
}
#Override
public boolean isValid(final Integer value, final ConstraintValidatorContext context) {
// this.validationService is always NULL!
Assert.notNull(this.validationService, "the validationService must not be null");
return false;
}
}
Injected ValidationService:
#Service
public class ValidationService {
public void test(final Foo foo) {
System.out.println(foo);
}
}
Spring boot application and configuration:
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(final String[] args) {
final ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
final FooService service = context.getBean(FooService.class);
service.foo(new Foo());
}
#Bean
public static LocalValidatorFactoryBean validatorFactory() {
return new LocalValidatorFactoryBean();
}
#Bean
public static MethodValidationPostProcessor validationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
relevant maven pom:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.1.9.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<start-class>demo.Application</start-class>
<java.version>1.7</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
I'm using the LocalValidatorFactoryBean with the default SpringConstraintValidatorFactory.
But why the dependency injection is not working in the ConstraintValidator and the ValidationService could not be autowired?
By the way if I don't use #Validated at the service, inject in opposite the spring or javax Validator interface and call manually "validator.validate" the dependency injection will work. But I don't want to call the validate method in every service manually.
Many thanks for help :)
I have fought the same problem in Spring Boot environment and I found out that Hibernate internal implementation got in instead of the configured Spring's one. When the application started, debugger caught a line with the Spring's factory but later in runtime there was Hibernate's one. After some debugging, I came to the conclusion that MethodValidationPostProcessor got the internal one. Therefore I configured it as follows:
#Bean
public Validator validator() {
return new LocalValidatorFactoryBean();
}
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor(Validator validator) {
MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
methodValidationPostProcessor.setValidator(validator);
return methodValidationPostProcessor;
}
Note the setter for validator - it did the job.
I had the same issue. The problem is arises because Hibernate is finding and applying the validators instead of Spring. So you need to set validation mode to NONE when configuring Hibernate:
#Bean(name="entityManagerFactory")
public LocalContainerEntityManagerFactoryBean
localContainerEntityManagerFactoryBean(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean lcemfb =
new LocalContainerEntityManagerFactoryBean();
lcemfb.setDataSource(dataSource);
lcemfb.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
lcemfb.setValidationMode(ValidationMode.NONE);
// Continue configuration...
If you have confgured a LocalValidationFactoryBean Spring will pick up any validators annotated with #Component and autowire them.
This is what worked for me. I had to used the #Inject tag.
public class FooValidator implements ConstraintValidator<FooValid, Integer> {
private ValidationService validationService;
#Inject
public FooValidator(ValidationService validationService){
this.validationService = validationService;
}
#Override
public void initialize(final FooValid constraintAnnotation) {
// TODO Auto-generated method stub
}
#Override
public boolean isValid(final Integer value, final ConstraintValidatorContext context) {
// this.validationService is always NULL!
Assert.notNull(this.validationService, "the validationService must not be null");
return false;
}
}