#RepositoryRestController custom controller with RepositoryRestMvcConfiguration and AbstractAnnotationConfigDispatcherServletInitializer - java

I am trying to implement custom controller to handle method defined in custom repository to be able to expose resources using this method via REST (according to Implementing custom methods of Spring Data repository and exposing them through REST).
Here is the configuration and other relevant code:
public class ApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {ApplicationConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {RepositoryRestMvcConfiguration.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/api/*"};
}
}
ApplicationConfig:
#Configuration
#EnableJpaRepositories
#EnableTransactionManagement
public class ApplicationConfig {
#Bean
public DataSource dataSource() {
// data source settings
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
// em factory settings
}
#Bean
public PlatformTransactionManager transactionManager() {
// tx mng settings
}
}
ExperimentRepository:
#RepositoryRestResource
public interface ExperimentRepository extends PagingAndSortingRepository<Experiment, Long>,
ExperimentRepositoryCustom {
#RestResource(rel = "byName", path = "byName")
Page<Experiment> findByNameContaining(#Param("name") String name, Pageable pageable);
}
ExperimentRepositoryCustom:
public interface ExperimentRepositoryCustom {
Page<Experiment> findUsingCustomFilter(...);
}
ExperimentRepositoryImpl:
public class ExperimentRepositoryImpl implements ExperimentRepositoryCustom {
#Override
public Page<Experiment> findUsingCustomFilter(...) {
// search for experiment based on given filter
}
}
ExperimentController:
#RepositoryRestController
public class ExperimentController {
#Autowired
private ExperimentRepository experimentRepository;
#RequestMapping(value = "/experiments/search/findByFilter", method= RequestMethod.GET)
#ResponseBody
public ResponseEntity<Experiment> searchUsingFilter(#RequestParam Long id) {
// for test purpose call method from CrudRepository (will be replaced by findUsingCustomFilter(...))
return new ResponseEntity(experimentRepository.findOne(id), HttpStatus.OK);
}
}
Project structure:
basepackage.model.Experiment
basepackage.repository.ExperimentController
basepackage.repository.ExperimentRepository
basepackage.repository.ExperimentRepositoryImpl
basepackage.repository.ExperimentRepositoryCustom
basepackage.ApplicationInitializer
basepackage.ApplicationConfig
All resources exposed by links automatically generated based on used Repositories are accessible with no problem but calling GET method on http://localhost:8080/app/api/experiments/search/findByFilter?id=1 ends with 404 (resources exposed by links automatically generated based on used Repositories work fine). I assume that ExperimentController is not registered in Spring container or I am missing some additional settings regarding controller method. Any suggestions?
Thanks in advance!

You need configuration to load your Controllers. One way to do this is to add a configuration class to load you Controllers and add it to your ApplicationInitializer.
//PLACE THIS IN A PACKAGE WHERE YOUR CONTROLLERS ARE
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
#ComponentScan
#Import(RepositoryRestMvcConfiguration.class)
public class WebConfig {
}
Change you ApplicationInitializer to
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import com.spring.data.rest.test.web.WebConfig;
public class ApplicationInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { ApplicationConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/api/*" };
}
}

Related

Spring Data Rest #RepositoryRestController and #RequestMapping

I want to override #RepositoryRestResource autogenerated controller methods using #RepositoryRestController having set the SDR's Base Path
to "/api".
Spring Data Rest 3.0 (and earlier) says:
"This controller [as shown in the snippet] will be served from the same API base path defined in RepositoryRestConfiguration.setBasePath that is used by all other RESTful endpoints (e.g. /api)".
https://docs.spring.io/spring-data/rest/docs/3.0.1.RELEASE/reference/html/#customizing-sdr.overriding-sdr-response-handlers (chapter 15.4)
This code snippet DOES NOT have a #RequestMapping on the class level, though.
My SDR app is configured with RepositoryRestConfiguration object
config.setBasePath("/api");
and yet #RepositoryRestController doesn't override SDR's autogenerated controller methods.
Please consider the accepted answear to this post:
Spring Data Rest controllers: behaviour and usage of #BasePathAwareController, #RepositoryRestController, #Controller and #RestController
Please help me understand this! :)
AppConf.java:
#Configuration
#Import(value = {DataConf.class})
#EnableWebMvc
#ComponentScan(value = "pl.mydomain.controller")
public class AppConf
{
#Bean
public RepositoryRestConfigurer repositoryRestConfigurer() {
return new RepositoryRestConfigurerAdapter() {
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.setBasePath("/api");
}
};
}
}
TokenController.java:
#RepositoryRestController
public class TokenController
{
private TokenRepository repository;
#Autowired
public TokenController(TokenRepository tokenRepository) {
this.repository = tokenRepository;
}
#RequestMapping(method = GET, path = "/tokens")
public #ResponseBody ResponseEntity<?> tokens()
{
return ResponseEntity.ok("Hello");
}
}
TokenRepository.java:
#RepositoryRestResource(path = "tokens")
public interface TokenRepository extends CrudRepository<Token, Long>{
}
The key to resolve the above dilemma was configuring the project in a correct fashion. That is, to put #ComponentScan in the class passed to AbstractAnnotationConfigDispatcherServletInitializer::getServletConfigClasses() method (not in AppConf.java passed to getRootConfigClasses()).
DispatcherConf.java:
public class DispatcherConf extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {AppConf.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] {WebConf.class}; // !!!
}
#Override
protected String[] getServletMappings() {
return new String[] {"/*"};
}
}
AppConf.java:
#Configuration
#Import({DataConf.class})
public class ApplicationConf
{
#Bean
public RepositoryRestConfigurer repositoryRestConfigurer() {
return new RepositoryRestConfigurerAdapter() {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.setBasePath("/api"); // !!!
}
};
}
}
DataConf.java:
#Configuration
#EnableJpaRepositories(basePackages = {
"pl.example.data.repository"
})
#EnableTransactionManagement
public class DataConf
{ ... }
WebConf.java:
#Import(RepositoryRestMvcConfiguration.class)
#ComponentScan({"pl.example.api.controller"}) // !!!
public class WebConf {
}
Even if I solved the riddle I don't understand why it was an issue. The rather that https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/ComponentScan.html states:
Annotation Type ComponentScan onfigures component scanning directives
for use with #Configuration classes.

Running Spring web from IDE vs from jar file

I am trying to create a simple web application that will use angular in FE and Spring in BE.
Everything works if I run it from my IDE (IntelliJ IDEA 2017.2), but if I start the jar file it cant find the jsp page and shows an Whitelabel Error Page.
Its gradle build and thease are the dependencies
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-rest')
compile('org.springframework.boot:spring-boot-starter-web')
testCompile('org.springframework.boot:spring-boot-starter-test')
compile 'javax.servlet:jstl:1.2'
}
ChartConfiguration.java
#Configuration
#EnableWebMvc
public class ChartConfiguration extends WebMvcConfigurerAdapter{
#Override
public void configureViewResolvers(ViewResolverRegistry registry) {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
registry.viewResolver(viewResolver);
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
ChartInitializer.java
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.Filter;
public class ChartInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { ChartConfiguration.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
protected Filter[] getServletFilters() {
Filter [] singleton = { new CORSFilter() };
return singleton;
}
}
IndexController.java
#Controller
#RequestMapping("/")
public class IndexController {
#RequestMapping(method = RequestMethod.GET)
public String getIndexPage() {
return "ChartManagement";
}
}
And my jsp page contains only a tag, nothing else.
What am I doing wrong, I cant figure it out?
So I made it work but with a little bit different configuration. I have removed
ChartConfiguration.java
ChartInitializer.java
Everything inside main/resources folder
And now I have created a folder in main/resources called templates, and inside it is index.html
Also I have added a new dependency
compile("org.springframework.boot:spring-boot-starter-thymeleaf")
And changed IndexController to
#Controller
#RequestMapping("/")
public class IndexController {
#RequestMapping(method = RequestMethod.GET)
public String getIndexPage() {
return "index";
}
}

how to Migrate AbstractAnnotationConfigDispatcherServletInitializer in Spring boot?

Hi I am new to spring boot currently I am trying to migrate my maven based web application to spring boot.
I was able to migrate all other configuration file except the below mentioned class:- ApplicationInitializer
public class ApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { Application.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
protected Filter[] getServletFilters() {
Filter [] singleton = { new CORSFilter()};
return singleton;
}
}
I found one reference link similar to this Reference link which does the same work but I am not sure how to do this for my case.
Can anyone help me to figure it out that how to register my filter with dispatcher servlet.
Thanks in advance.
To register your filter with a dispatcher using SpringBootServletInitializer (that's what you refered to) you need to add FilterRegistrationBean. It may look like that:
public class Application extends SpringBootServletInitializer
{
#Override
protected SpringApplicationBuilder configure( SpringApplicationBuilder application )
{
return application.sources( Application.class );
}
#Bean
public FilterRegistrationBean filterRegistrationBean( ServletRegistrationBean servletRegistrationBean )
{
return new FilterRegistrationBean( new CORSFilter(), servletRegistrationBean );
}
public static void main( String[] args )
{
SpringApplication.run( Application.class, args );
}
}
Be aware that above code replaces your ApplicationInitializer.

Use #Autowired in Spring MVC and JavaConfig

I have spring mvc project and i don't use #Autowired because my object always is null. How me load JavaConfig for using #Autowired, i do not use any *.xml file.
This is my controller with #Autowired field for service.
#Controller
public class WebController {
#Autowired
private ServiceWeb serviceWeb;
public void setServiceWeb(ServiceWeb serviceWeb) {
this.serviceWeb = serviceWeb;
}
...
}
This is my AbstractAnnotationConfigDispatcherServletInitializer
public class ServletInit extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { SpringRootConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { SpringWebConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
SpringRootConfig & SpringWebConfig
#Configuration
#ComponentScan({"web.controller"})
public class SpringRootConfig {
}
#EnableWebMvc
#Configuration
#ComponentScan({ "web.controller"})
#Import({SecurityConfig.class})
public class SpringWebConfig extends WebMvcConfigurerAdapter{
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
}
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver
= new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
Class for #Autowiring
#Configuration
public class ConfigurationBean {
#Bean
public ServiceWeb serviceWeb(){
return new ServiceWebImpl();
}
}
Register context for spring, but where need write Init.class for loading this config ?
public class Init implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(ConfigurationBean.class);
servletContext.addListener(new ContextLoaderListener(ctx));
ctx.setServletContext(servletContext);
}
}
Pls try this...
#Service
public class ServiceWebImpl implements ServiceWeb {
}
ServiceWeb bean is created inside ConfigurationBean class and its not visible at spring context level for autowiring.

Spring Config from XML to Java not working

I cannot seem to get simple Spring application to work with JavaConfig.
public class WebApp extends AbstractAnnotationConfigDispatcherServletInitializer {
private static final Logger logger = Logger.getLogger(WebApp.class);
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[0];
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{ WebAppConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[]{ "/" };
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
logger.debug("onStartup");
super.onStartup(servletContext);//MUST HAVE
servletContext.setInitParameter("defaultHtmlEscape", "true");
}
#Configuration
#EnableWebMvc
#ComponentScan("com.doge.controller")
public static class WebAppConfig extends WebMvcConfigurerAdapter {
}
}
And controller:
package com.doge.controller;
#RestController
public class HelloController {
#RequestMapping("/")
public String sayHello() {
System.out.println("something");
return "index";
}
}
I always get 404 on "localhost:8080/Build" nor "localhost:8080".
Nothing is ever logged nor printed, just "INFO: Server startup in 538 ms".
There are few options of initialize Spring web application. The easiest is like below:
public class SpringAnnotationWebInitializer extends AbstractContextLoaderInitializer {
#Override
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext applicationContext =
new AnnotationConfigWebApplicationContext();
applicationContext.register(WebAppConfig.class);
return applicationContext;
}
}
Other options can be found here: http://www.kubrynski.com/2014/01/understanding-spring-web-initialization.html

Categories