Spring boot can I replace ResourceHttpRequestHandler with my own implementation? - spring

I am running into this issue since I have an app that has restful services but I need to serve up some static ones too. In this app I also use the EnableSwagger2 annotation. It looks like this class is public and I could subclass it but I am not sure how to configure that. My goal is to override this line so I can have control over 404s.

So I finally did do this and it worked successfully for me. The config got me for a bit but here it is. Say you subclass ResourceHttpRequestHandler with one named CustomResourceHandler. In my Application.java this wired it up properly:
#Bean
public ResourceHttpRequestHandler resourceHttpRequestHandler() {
ResourceHttpRequestHandler requestHandler = new CustomResourceHandler();
requestHandler.setLocations(Arrays.<Resource>asList(applicationContext.getResource("/")));
return requestHandler;
}
#Bean
public SimpleUrlHandlerMapping sampleServletMapping(){
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Integer.MAX_VALUE - 2);
Properties urlProperties = new Properties();
urlProperties.put("/**", "resourceHttpRequestHandler");
mapping.setMappings(urlProperties);
return mapping;
}
#Autowired
ApplicationContext applicationContext;
This Answer helped me with the proper config of the mapper.
This Answer about context helped me get the locations set properly in my handler. I was setting them a different way but it wasn't working 100% properly for all my resources

Related

How to make globally shared objects available to freemarker templates in Spring Boot 2

What is the best way of making global shared objects available to freemarker templates when using Spring Boot 2.x, without losing Spring Boot's FreeMarker auto configuration?
The underlying mechanism for doing this is Spring Boot's FreeMakerConfigurer.setFreemarkerVariables, which in turn calls FreeMarker's Configuration.setAllSharedVariables
However, there is no obvious way (to me) to modify the FreeMarkerConfigurer that is setup by FreeMarkerServletWebConfiguration beyond the predefined freemarker properties that Spring Boot supports. (Search for "freemarker" here).
A common approach is to create a custom FreemarkerConfigurer bean, but I believe that then loses some of the auto configuration provided by spring boot, especially around the handling of various external properties.
One option that seems to work is to use a BeanPostProcessor like this:
public class CustomFreeMarkerConfig implements BeanPostProcessor {
Object sharedWithAllFreeMarkerTemplatesObj = new Object();
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof FreeMarkerConfigurer) {
FreeMarkerConfigurer configurer = (FreeMarkerConfigurer) bean;
Map<String, Object> sharedVariables = new HashMap<>();
sharedVariables.put("obj", sharedWithAllFreeMarkerTemplatesObj);
configurer.setFreemarkerVariables(sharedVariables);
}
return bean;
}
}
It seems like there should be a cleaner way of doing it, perhaps by somehow extending or configuring FreeMarkerConfigurationFactory, but I haven't been able to find it.
I found a solution from spring git
Spring Boot 2.0 breaks the solution provided by #wo8335224, as FreeMarkerWebConfiguration is replaced by FreeMarkerServletWebConfiguration, which is unfortunately package-private and thus cannot be subclassed.
A currently working solution is to configure freemarker.template.Configuration bean:
#Configuration
public class FreemarkerConfig {
public FreemarkerConfig(freemarker.template.Configuration configuration) throws TemplateModelException {
configuration.setSharedVariable("name", "whatever type of value");
}
}
Internally FreeMarkerConfigurer#setFreemarkerVariables delegates its work to freemarker.template.Configuration#setAllSharedVariables.

JAX-RS, Spring & ServletConfig: How to access Servlet Config in Configurator

I have troubles getting a javax.servlet.ServletConfig into a class annotated with org.springframework.context.annotation.Configuration.
My team decided that we should use spring for dependency injection and I'm trying to use it to migrate one of our simple Rest services.
My constraints are:
JAX-RS: We have several REST Services implemented JAX-RS and we don't really want to change that.
Not bound to a specific implementation of JAX-RS (Jersey & RESTEasy work fine for us and we can change from one to the other without changing underlying code)
Import as few dependencies as possible from spring: at the moment I import only org.springframework:spring-context from the spring project.
No API breakage: Deprecated is fine but the service should keep working during the transition, using our old way of doing things.
A string parameter is defined in the service's web.xml. I need to get it, instantiate a Bean with it and inject the resulting bean at several place in the code.
I don't want to mess with Spring Boot/MVC/... as the service already works and I just want the Dependency Injection part.
What I already have:
The code use javax.ws.rs.core.Application, with a class that look like that:
public class MyApplication extends Application {
#Context
private ServletConfig cfg;
public DSApplication() {
}
#Override
public Set<Class<?>> getClasses() {
return new HashSet<>();
}
#Override
public Set<Object> getSingletons() {
Set<Object> set = new HashSet<>();
String injectionStr = cfg.getInitParameter("injection");
boolean injection = false;
if (null != injectionStr && !injectionStr.isEmpty()) {
injection = Boolean.valueOf(injectionStr);
}
if (injection) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
DSServiceProducer.class,
CContextBeanProvider.class
);
IDSService service = context.getBean(IDSService.class);
set.add(service);
} else {
set.add(new DSService()); //Old way
}
return set;
}
}
I need the servlet config in CContextBeanProvider, which look like:
#Configuration
public class CContextBeanProvider {
private ServletConfig cfg; // How to get this here ?
#Bean
public CContextBean cContextBean() {
String bean = cfg.getInitParameter("cpuContext");
return new CContextBean(bean);
}
}
CContextBean is a setting bean initialized from a string found in the web.xml of the service.
Is it possible ?
Do you have any idea how ?
Would it be easier with CDI, knowing that we run on base Tomcat ? (I've already find this if I need to use tomcat with CDI)
Could you please try to add all jersey CDI related jars to your applications ?

spring boot 1.3.3 create multiple resttemplate per env

I am on spring boot 1.3.3 version, I have a requirement where my spring boot application need to call endpoint(s) based on env passed,
which means if env passed as Dev i would need to call devendpoint,
if env passed as Dev1 then need to call dev1endpoint and so on.
So how can I do this ?
Do I need to create multiple restTemplate instances ?
Should I construct the resttemplate dynamically based on env passed ?
As part of constructing resttemplate i would also need to add appllicable interceptor based on env selected.
Plesae suggest.
You can have two beans of the same class. One can be labeled as the primary, and the use on the #Autowired can specify which one to use with the #Qualifier.
Example:
#Configuration
public class MyConfig {
#Bean
#Primary
public RestTemplate typicalConfig() {
// various configs on your rest template
return new RestTemplate();
}
#Bean
public RestTemplate lessTypical() {
// various alternate configurations
return new RestTemplate();
}
}
Now in your service class:
#Service
public class MyService {
#Autowired
RestTemplate typicalRestTemplate;
#Autowired
#Qualifier("lessTypical")
private RestTemplate alternateRestTemplate;
...
}
correct me if I didn't understand your question. I understand that you are going to have different environments but you are going to change this endpoints in runtime depends on some information or whatever, but I don't understand the part when you said you have to create multiple instances of restTemplate for that environments, from my experience on spring boot applications you don't have to do things like that, You just have to create your restTemplate configuration bean.
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
And then injected that object on your services class and do whatever you want with them. I recommend you to read the follow article about restTempalte may this could help you http://www.baeldung.com/rest-template

Spring Boot with Two MVC Configurations

I have a Spring Boot app with a REST API, using Jackson for the JSON view configuration. It works great and I can get all the Spring Boot goodness.
However, I need to add an additional REST API that is similar but with different settings. For example, among other things, it needs a different Jackson object mapper configuration because the JSON will look quite a bit different (e.g. no JSON arrays). That is just one example but there are quite a few differences. Each API has a different context (e.g. /api/current and /api/legacy).
Ideally I'd like two MVC configs mapped to these different contexts, and not have to give up any of the automatic wiring of things in boot.
So far all I've been able to get close on is using two dispatcher servlets each with its own MVC config, but that results in Boot dropping a whole bunch of things I get automatically and basically defeats the reason for using boot.
I cannot break the app up into multiple apps.
The answer "you cannot do this with Boot and still get all its magic" is an acceptable answer. Seems like it should be able to handle this though.
There's several ways to achieve this. Based on your requirement , Id say this is a case of managing REST API versions.
There's several ways to version the REST API, some the popular ones being version urls and other techniques mentioned in the links of the comments.
The URL Based approach is more driven towards having multiple versions of the address:
For example
For V1 :
/path/v1/resource
and V2 :
/path/v2/resource
These will resolve to 2 different methods in the Spring MVC Controller bean, to which the calls get delegated.
The other option to resolve the versions of the API is to use the headers, this way there is only URL, multiple methods based on the version.
For example:
/path/resource
HEADER:
X-API-Version: 1.0
HEADER:
X-API-Version: 2.0
This will also resolve in two separate operations on the controller.
Now these are the strategies based on which multiple rest versions can be handled.
The above approaches are explained well in the following: git example
Note: The above is a spring boot application.
The commonality in both these approaches is that there will need to be different POJOS based on which Jackson JSON library to automatically marshal instances of the specified type into JSON.
I.e. Assuming that the code uses the #RestController [org.springframework.web.bind.annotation.RestController]
Now if your requirement is to have different JSON Mapper i.e. different JSON mapper configurations, then irrespective of the Spring contexts you'll need a different strategy for the serialization/De-Serialization.
In this case, you will need to implement a Custom De-Serializer {CustomDeSerializer} that will extend JsonDeserializer<T> [com.fasterxml.jackson.databind.JsonDeserializer] and in the deserialize() implement your custom startegy.
Use the #JsonDeserialize(using = CustomDeSerializer.class) annotation on the target POJO.
This way multiple JSON schemes can be managed with different De-Serializers.
By Combining Rest Versioning + Custom Serialization Strategy , each API can be managed in it's own context without having to wire multiple dispatcher Servlet configurations.
Expanding on my comment of yesterday and #Ashoka Header idea i would propose to register 2 MessageConverters (legacy and current) for custom media types. You can do this like that:
#Bean
MappingJackson2HttpMessageConverter currentMappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
// set features
jsonConverter.setObjectMapper(objectMapper);
jsonConverter.setSupportedMediaTypes(Arrays.asList(new MediaType("json", "v2")));
return jsonConverter;
}
#Bean
MappingJackson2HttpMessageConverter legacyMappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
// set features
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
Pay attention to the custom media-type for one of the converters.
If you like , you can use an Interceptor to rewrite the Version-Headers proposed by #Ashoka to a custom Media-Type like so:
public class ApiVersionMediaTypeMappingInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
try {
if(request.getHeader("X-API-Version") == "2") {
request.setAttribute("Accept:","json/v2");
}
.....
}
}
This might not be the exact answer you were looking for, but maybe it can provide some inspiration. An interceptor is registered like so.
If you can live with a different port for each context, then you only have to overwrite the DispatcherServletAutoConfiguration beans. All the rest of the magic works, multpart, Jackson etc. You can configure the Servlet and Jackson/Multipart etc. for each child-context separately and inject bean of the parent context.
package test;
import static org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME;
import static org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#Configuration
#EnableAutoConfiguration(exclude = {
Application.Context1.class,
Application.Context2.class
})
public class Application extends WebMvcConfigurerAdapter {
#Bean
public TestBean testBean() {
return new TestBean();
}
public static void main(String[] args) {
final SpringApplicationBuilder builder = new SpringApplicationBuilder().parent(Application.class);
builder.child(Context1.class).run();
builder.child(Context2.class).run();
}
public static class TestBean {
}
#Configuration
#EnableAutoConfiguration(exclude = {Application.class, Context2.class})
#PropertySource("classpath:context1.properties")
public static class Context1 {
#Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
DispatcherServlet dispatcherServlet() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
// custom config here
return dispatcherServlet;
}
#Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
ServletRegistrationBean dispatcherServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(), "/test1");
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
// custom config here
return registration;
}
#Bean
Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder(TestBean testBean) {
System.out.println(testBean);
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
// custom config here
return builder;
}
}
#Configuration
#EnableAutoConfiguration(exclude = {Application.class, Context1.class})
#PropertySource("classpath:context2.properties")
public static class Context2 {
#Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
DispatcherServlet dispatcherServlet() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
// custom config here
return dispatcherServlet;
}
#Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
ServletRegistrationBean dispatcherServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(), "/test2");
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
// custom config here
return registration;
}
#Bean
Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder(TestBean testBean) {
System.out.println(testBean);
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
// custom config here
return builder;
}
}
}
The context1/2.properties files currently only contain a server.port=8080/8081 but you can set all the other spring properties for the child contexts there.
In Spring-boot ypu can use different profiles (like dev and test).
Start application with
-Dspring.profiles.active=dev
or -Dspring.profiles.active=test
and use different properties files named application-dev.properties or application-test.properties inside your properties directory.
That could do the problem.

Using Quartz with Spring Boot - injection order changes based upon return type of method

I am trying to get Quartz working with Spring Boot, and am not managing to get the injection working correctly. I am basing myself on the example shown here
Here is my boot class:
#ComponentScan
#EnableAutoConfiguration
public class MyApp {
#Autowired
private DataSource dataSource;
#Bean
public JobFactory jobFactory() {
return new SpringBeanJobFactory();
}
#Bean
public SchedulerFactoryBean quartz() {
final SchedulerFactoryBean bean = new SchedulerFactoryBean();
bean.setJobFactory(jobFactory());
bean.setDataSource(dataSource);
bean.setConfigLocation(new ClassPathResource("quartz.properties"));
...
return bean;
}
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
When the quartz() method is invoked by Spring, dataSource is null. However, if I change the return type of the quartz() method to Object, dataSource is correctly injected with the datasource created by reading application.properties, the bean is built, everything works and I get a subsequent error saying that Quartz has been unable to retrieve any jobs from the database, which is normal as I haven't put the schema in place yet.
I have tried adding a #DependsOn("dataSource") annotation on the quartz() method but that doesn't make any difference.
This class is the only class annotated with #Configuration.
Here are my dependencies (I'm using Maven but present them like this for space reasons):
org.springframework.boot:spring-boot-starter-actuator:1.0.0.RC4
org.springframework.boot:spring-boot-starter-jdbc:1.0.0.RC4
org.springframework.boot:spring-boot-starter-web:1.0.0.RC4
org.quartz-scheduler:quartz:2.2.1
org.springframework:spring-support:2.0.8
And the parent:
org.springframework.boot:spring-boot-starter-parent:1.0.0.RC4
Finally the content of quartz.properties:
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.class=org.springframework.scheduling.quartz.LocalDataSourceJobStore
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
What am I doing wrong?
(I have seen this question, but that question initialises the datasource in the #Configuration class)
Your app starts up (with a schema error, which is expected) if I use "org.springframework:spring-context-support:4.0.2.RELEASE" ("org.springframework:spring-support:2.0.8" if it ever existed must be nearly 10 years old now and certainly isn't compatible with Boot or Quartz 2).

Resources