Apache CXF (JAX-RS) with Spring Javaconfig and Jackson - java

How to setup Apache CXF with JAX-RS Spring Javaconfig and Jackson?
I have read
http://cxf.apache.org/docs/springboot.html#SpringBoot-SpringBootCXFJAX-RSStarter and
http://cxf.apache.org/docs/jax-rs-data-bindings.html#JAX-RSDataBindings-Jackson
It advises to use
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider"/>
</jaxrs:providers>
but I want to stay away from XML and use plain javaconfig.
My current configuration is
#SpringBootApplication
public class Application {
#Autowired
private Bus bus;
public static void main(String[] args) {
SpringApplication.run(Application .class, args);
}
#Bean
public Server rsServer() {
JAXRSServerFactoryBean endpoint = new JAXRSServerFactoryBean();
endpoint.setBus(bus);
endpoint.setAddress("/");
endpoint.setServiceBeans(Arrays.<Object>asList(new MyService()));
return endpoint.create();
}
}
Andpoint defined in my class MyService works, but when he is invoked I am getting message
org.apache.cxf.jaxrs.utils.JAXRSUtils : No message body writer has
been found for class MyServiceResponse,
ContentType: application/json;charset=UTF-8

You need to add a json Provider bean. This will act as messageProvider. Your Application class should look like this.
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider
#SpringBootApplication
public class Application {
#Autowired
private Bus bus;
public static void main(String[] args) {
SpringApplication.run(Application .class, args);
}
#Bean
public Server rsServer() {
List<? extends Object> providers = new ArrayList<>();
providers.add(getJsonProvider());
JAXRSServerFactoryBean endpoint = new JAXRSServerFactoryBean();
endpoint.setProviders(providers);
endpoint.setBus(bus);
endpoint.setAddress("/");
endpoint.setServiceBeans(Arrays.asList(new MyService()));
return endpoint.create();
}
#Bean
public JacksonJsonProvider getJsonProvider() {
new JacksonJsonProvider();
}
}

Related

Request to Spring Controller returns 404 not found

I am trying to set up a Spring MVC app but every time I call the http://localhost:9001/tasks API from postman I get the following error:
Here is my code:
#SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class TaskManagerApplication {
public static void main(String[] args) {
SpringApplication.run(TaskManagerApplication.class, args);
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:4200");
}
};
}
}
TaskRepository:
#Path("tasks")
#ApiIgnore
#Component
#AllArgsConstructor
public class TaskResource {
private final TaskService taskService;
#GET
#Produces(APPLICATION_JSON)
public List<Task> getAllTasks() {
return taskService.getTasks();
}
TaskService:
#Service
#RequiredArgsConstructor
public class TaskService {
private final TaskRepository taskRepository;
public List<Task> getTasks() {
return taskRepository.findAll();
}
Project Structure:
You are using JAX-RS in spring boot. Spring handles rest in its own way, if you want to use JAX-RS instead of Springs Rest Annotations, you need to do some extra configurations.
First, you need to add a JAX-RS dependency in your build.gradle or pom.xml file. I guess you have already done that. Jersey is one of the JAX-RS implementation, if you want to add this, you need to do the following.
build.gradle
implementation "org.springframework.boot:spring-boot-starter-jersey"
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
After that, you need to register the JAX-RS endpoints with Spring. I guess you missed this step.
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.context.annotation.Configuration;
#Configuration
public class JaxrsConfig extends ResourceConfig {
public JaxrsConfig() {
register(TaskResource.class);
}
}
After this, your JAX-RS endpoints will be registered with spring.
But I will suggest you to follow spring annotations if you are using spring. If you use spring annotations your code will look like this.
#RestController
#RequestMapping(path = "tasks")
public class TaskResource {
#GetMapping(path = "", produces = MediaType.APPLICATION_JSON_VALUE)
public List<String> getAllTasks() {
return Arrays.asList("a","b");
}
}
Also you will need to remove JAX-RS from spring to use this Spring MVC annoatations to work.

#Autowired object is null when working with #SpringBootTest

So I am trying to autowire my http object in my test class and I have tried to integrate with #SpringBootTest however my http object still remains null.
My test class looks like this.
//#RunWith(SpringRunner.class)
#SpringBootTest(classes=Http.class)
public class GetItemTests {
private static final Logger LOGGER = LoggerFactory.getLogger(GetItemTests.class);
#Autowired
private Http httpClass;
}
My SpringBootMain class looks like this
#SpringBootConfiguration
#SpringBootApplication
public class SpringBootMain implements CommandLineRunner {
#Bean
ResourceConfig resourceConfig() {
return new ResourceConfig().registerClasses(Version1Api.class,TokenUtilityClass.class, Paypal.class);
}
#Override
public void run(String... args) throws Exception {
//test.authenticationToken();
}
public static void main(String[] args) {
SpringApplication.run(SpringBootMain.class);
}
}
I have tried running with the SpringRunner as well as this but I receive errors about failing to load the application context.
Unless your Http class is annotated with #Component (meaning it is a bean maintained by the Spring IoC Container) you will not be able to #Autowire it in the way you wrote.
Please also post the exception you are getting and the implementation of your Http class so we can potentially provide further help.

Can not call interceptor when using #AutoConfigureAfter(WebMvcAutoConfiguration.class) and annotation-driven

While implementing the interceptors in my spring boot application I came across a weird problem which is as follows:
My Application class code:
#Import(CommonConfig.class)
#EnableAsync
#SpringBootApplication
#EnableSwagger2
#EnableScheduling
#EnableSpringDataWebSupport
#ImportResource({ "classpath:META-INF/applicationContext.xml" })
#AutoConfigureAfter(WebMvcAutoConfiguration.class)
public class Application extends SpringBootServletInitializer {
private static final Logger LOG = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
.......
................
}
#Bean
public TokenInterceptor intializeTokenInterceptor() {
System.out.println("Adding interceptors");
return new TokenInterceptor();
}
#Bean
public WebMvcConfigurerAdapter adapter() {
return new WebMvcConfigurerAdapter() {
#Override
public void addInterceptors(InterceptorRegistry registry) {
System.out.println("Adding interceptors");
registry.addInterceptor(intializeTokenInterceptor()).addPathPatterns("/**");
super.addInterceptors(registry);
}
};
}
}
Inside my application-context I have mentioned tag (mvc:annotation-driven) as I need this for initializing my bean classes.
Now, if I test my application by removing the annotation-driven tag, my interceptors are getting called. However, if I keep this tag, my interceptors are not getting called, (which I believe is overriding the implementation of WebMvcConfigurerAdapter bean from WebMvcAutoConfiguration.class somehow).

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 Cloud Config and static content

I have a application that uses Spring cloud config (--spring.profiles.active=native) and also serves up some html pages within the same application. All is fine until I introduce static resources (src/main/resources/css/bootstrap-switch.css). The URL calls to http://localhost:8080/css/bootstrap-switch.css fails with this Exception:
{"timestamp":1438114326940,"status":406,"error":"Not Acceptable","exception":"org.springframework.web.HttpMediaTypeNotAcceptableException","message":"Could not find acceptable representation","path":"/css/bootstrap-switch.css"}
When I disable the #EnableConfigServer, the URL returns the CSS content. I am on Spring Cloud Config version 1.0.2.
Here's my minimalist code that can reproduce this issue:
#SpringBootApplication
#EnableConfigServer
public class Application {
public static void main(String args[]) {
SpringApplication.run(ApplicationConfiguration.class, args);
}
}
#Configuration
#SpringBootApplication
class ApplicationConfiguration {
#Bean
public TestController testController() {
return new TestController();
}
#Bean
public MvcController mvcController() {
return new MvcController();
}
}
#RestController
class TestController {
#RequestMapping("/test")
#ResponseBody
public String test() {
return "hello world";
}
}
#Controller
class MvcController {
#RequestMapping("/landing")
public String landingPage() {
return "landing";
}
}
Config server by default has an api that matches /*/*. You can move the root of the api by changing spring.cloud.config.server.prefix=myroot.

Categories