JAX-RS, Jackson and JodaTime: not working automatic configuration - java

I'm trying to create a simple REST service with JAX-RS (Jersey), without using Spring. And I'm using Joda as date fields in my entity.
To configure automatic json mapping, I create a JsonMapperProvider, where I add JodaModule:
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class JsonMapperProvider implements ContextResolver<ObjectMapper> {
final ObjectMapper objectMapper;
public JsonMapperProvider() {
objectMapper = new ObjectMapper();
objectMapper.registerModule(new JodaModule());
}
#Override
public ObjectMapper getContext(Class<?> arg0) {
return objectMapper;
}
}
This is my Resource class:
#Path("users")
public class UserController {
#Inject
private UserService userService;
#GET
#Path("/{id}")
#Produces(MediaType.APPLICATION_JSON)
public User getUserById(#PathParam("id") Long id) {
return userService.findById(id);
}
}
And I'm using a "no web.xml" configuration, with this class:
#ApplicationPath("api")
public class RestApplication extends ResourceConfig {
}
But it doesn't work... the LocalDate field in User entity is always returned empty.
The only workaround I found is to register all the components (including JacksonFeature class from jersey-media-json-jackson) in the ResourceConfig class, like this:
#ApplicationPath("api")
public class RestApplication extends ResourceConfig {
public RestApplication() {
super(
UserController.class,
JsonMapperProvider.class,
JacksonFeature.class
);
}
}
Is there another solution to this problem? I'd rather not to register all my services and other stuff in this class manually...

No, this is how it's supposed to work. You can also override the methods in javax.ws.rs.core.Application instead of extending ResourceConfig

Related

Jax-rs ContextResolver for custom ObjectMapper

I am not sure how to register my custom objectMapper that I created below as a bean and inject it as dependency into other objects via constructor, or Autowire
#SpringBootApplication
public class DemoApplication {
#Bean
//how to register it as a bean here and inject wherever I need to via #Inject or #Autowire
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
#Provider
public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
private final ObjectMapper objectMapper = new ObjectMapper();
public ObjectMapperProvider() {
this.objectMapper.disable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
}
#Override
public ObjectMapper getContext(final Class<?> type) {
return objectMapper;
}
}
Be careful with that. You are mixing Jax-RS and Spring, but you have to know something: Spring does not implement fully the Jax-RS specification... The reason ? Spring MVC was developed about the same time as JAX-RS, and after JAX-RS was released, they never migrate to implement this (who would have anyway) ?
The best way to declare your own ObjectMapper with Spring would be the following:
#SpringBootApplication
public class DemoApplication {
#Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
// DO what you want;
return objectMapper;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
}
Then, you can use #Autowired to inject your ObjectMapper in the class that needs it. (check this link if you want: Configuring ObjectMapper in Spring)
Hope it helps.

Jersey Configuration Not identifying service and dao classes

This is my jersey config class
#ApplicationPath("services")
public class JerseyApplication extends ResourceConfig{
public JerseyApplication() {
packages("com.ems");
register(EmployeeService.class);
}
}
Here autowiring of employeeService is giving a null pointer exception
#Path("/ems")
#Component
public class EmployeeRestController {
#Autowired
private EmployeeService employeeService;
#GET
#Path("/employees")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public List<Employee> getEmployees() {
return employeeService.getEmployees();
}
}
I have tried everything
In my employeeServiceImpl I have #service annotation
Still, it is not working.
To configure the dependency injection using the built in DI framework (HK2), you should use an AbstractBinder, as mentioned in some answers in Dependency injection with Jersey 2.0.
#ApplicationPath("services")
public class JerseyApplication extends ResourceConfig {
public JerseyApplication() {
packages("com.ems");
register(new AbstractBinder() {
#Override
protected void configure() {
bind(EmployeeService.class)
.to(EmployeeService.class)
.in(Singleton.class);
}
});
}
}
Secondly, you do not use the #Autowired annotation. This annotation is specifically for Spring. For standard injection with Jersey, just use the #Inject annotation. Also remove the #Component annotation, as this is also for Spring.
As an aside, if you do want to integrate Spring with Jersey, you should read Why and How to Use Spring With Jersey. It will break down what you need to understand about integrating the two frameworks.
You should register Controller not Service class.
Sample
#ApplicationPath("services")
public class JerseyApplication extends ResourceConfig{
public JerseyApplication() {
packages("com.ems");
register(EmployeeRestController.class);
}
}

Retrofit Autoconfiguration in Spring

I am currently writing a Spring Boot autoconfiguration for Retrofit 2. What I am trying to do is to write some sort of an interface builder that is able instantiate an interface that is annotated with some annotation for autowiring just like Spring Data does it with repositories. As I cannot find any resources on how to do this (or if it can even be done with Spring), I would like to ask for your thoughts on that. Below is for an interface that I would like to instantiate.
My replacement for #Repository is #Retrofit the rest is just "ordinary" code you would write for any Retrofit repository.
The kind of interface I would like to autowire:
#Retrofit
public interface Api {
#GET("usernames")
String[] getUsernames();
}
An example for autowiring:
#SpringBootApplication
public class TestApplication {
#Autowired
private Api api;
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
#Bean
CommandLineRunner runner() {
return args -> {
System.out.println(api.getUsernames());
};
}
}
As I said I found a solution for my problem.
First we need an auto configuration class that is loaded by Spring Boot - as stated here - by adding the file META-INF/spring.factories with the content that is shown below. This auto configuration loads a registrar which itself searches for classes annotated with #Retrofit via a component provider. At last the registrar creates instances of RetrofitFactoryBean for each instance that could be found while this factory bean creates the Retrofit proxies itself.
The auto configuration
#Configuration
#Import(RetrofitRegistrar.class)
public class RetrofitAutoConfiguration {
#Bean
#ConditionalOnMissingBean
public Retrofit retrofit() {
return new Retrofit.Builder().build();
}
}
META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
spring.retrofit.RetrofitAutoConfiguration
The imported registrar
public class RetrofitRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
#Setter
private BeanFactory beanFactory;
#Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
List<String> basePackages = AutoConfigurationPackages.get(this.beanFactory);
RetrofitComponentProvider provider = new RetrofitComponentProvider(registry);
basePackages.stream()
.map(provider::findCandidateComponents)
.flatMap(Set::stream)
.forEach(comp -> register(comp, registry));
}
private void register(BeanDefinition component, BeanDefinitionRegistry registry) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.
rootBeanDefinition(RetrofitFactoryBean.class);
builder.addConstructorArgValue(component.getBeanClassName());
registry.registerBeanDefinition(
component.getBeanClassName().toLowerCase(), builder.getBeanDefinition());
}
}
The component provider
class RetrofitComponentProvider extends ClassPathScanningCandidateComponentProvider {
#Getter
private BeanDefinitionRegistry registry;
public RetrofitComponentProvider(BeanDefinitionRegistry registry) {
super(false);
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
this.registry = registry;
addIncludeFilter(new AnnotationTypeFilter(Retrofit.class, true, true));
}
#Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return true;
}
}
The factory bean
#Component
#RequiredArgsConstructor
public class RetrofitFactoryBean extends AbstractFactoryBean<Object> {
#Getter
private final Class<?> objectType;
private final retrofit2.Retrofit retrofit;
#Override
protected Object createInstance() throws Exception {
return retrofit.create(objectType);
}
}
The #Getter, #Setter and #RequiredArgsConstructor annotations are provided by ProjectLombok
Let me ask you firstly to avoid reinventing the wheel by creating a new Spring annotation (yours here is #Retrofit. However, it is absolutely okay to use retrofit with spring there is nothing to prevent it. You can simply try to use an existing Spring annotation which can be #Component as you can see in this question
you can autowire your interface without facing problems.
Hope this helps.

Spring Boot Jersey static content with resources at "/"

I have a project setup with Spring Boot and Jersey, and I need to serve static content. The main difference with other similar questions is the fact that "mywebsite.com/" has to be a Jersey resource. This is my current JerseyConfig:
#ApplicationPath("/")
#Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("com.mywebsite.services.rest",
"com.mywebsite.services.publication");
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
And my publication resource:
#Component
#Path("/")
#Produces(MediaType.TEXT_HTML)
public class PublicacionResource {
#GET
public Template index() throws Throwable {
return generateTemplateWithParameters("instalaciones", null, null);
}
...
}
Jersey doesn't seem to be forwarding the 404 requests, as I cannot access static content.
Throw an Exception in generateTemplateWithParameters and forward it via ExceptionMapper
#Provider
public class MyExceptionProvider implements ExceptionMapper<MyException> {
#Override
public Response toResponse(final MyException exception) {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
Don't forget to register it in Jersey's ResourceConfig:
register(MyExceptionProvider.class);

#RequestBody with generic type

I'm having some problem with Spring MVC 4.2.6 and Jackson (2.7.3). I created an absctract #RestController class with a method that uses a generic type. I implement this abstract class passing the type of generic object. I use #RequestBody and the DispatcherServlet throws JsonMappingException (can not construct instance of my class). The MappingJackson2HttpMessageConverter in application context is configured.
Edit: More info added.
public abstract class AbsctractAnimalResource<DTO extends AnimalDTO> {
// Doesn't work. Throws JsonMappingException. (#RequestBody)
#RequestMapping(value = "/bar", method = POST, produces = APPLICATION_JSON_VALUE)
public ResponseEntity<?> bar(#RequestBody DTO dto) throws Exception {
return new ResponseEntity(dto, HttpStatus.CREATED);
}
}
#RestController
#RequestMapping("/cat")
public class CatResource extends AbsctractAnimalResource<CatDTO> {
}
#RestController
#RequestMapping("/dog")
public class DogResource extends AbsctractAnimalResource<DogDTO> {
}
interface AnimalDTO { }
public class CatDTO implements AnimalDTO { }
public class DogDTO implements AnimalDTO { }
After some debugs in Spring 4.2.6 classes I found this commentary:
https://github.com/spring-projects/spring-framework/blob/4.2.x/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java#L51
Spring 4.2.X and Jackson 2.7.X don't work together. I downgraded my Jackson version to 2.6.6 and now everything works. Spring 4.3.X works well with Jackson 2.7+.

Categories