Apache CXF + Spring Java config (no XML)
Asked Answered
S

5

19

Trying to deploy a JAX-WS endpoint using Tomcat 7 Maven plugin and CXF 2.7.8. As a matter of preference, I don't want to have any XML config for Spring or CXF. I see several blogs, articles, posts using cxf-servlet.xml and CXFServlet but none whatsoever completely using Java config. Looking into the CXFServlet source code, it looks for the cxf-servlet.xml or anything in the servlet context under the key 'config-location'. I tried programmatically registering the endpoint instead of in cxf-servlet.xml, but it doesn't work; I get a 404 when accessing the service. Any ideas?

@Configuration
@ImportResource({ "classpath:META-INF/cxf/cxf.xml" })
public class CXFConfig {
    @Autowired
    Bus cxfBus;

    // More code

    @Bean
    public Endpoint calculator() {
        EndpointImpl endpoint = new EndpointImpl(cxfBus, new Calculator());
        endpoint.setAddress("/CalculatorService");
        return endpoint;
    }
}
Stencil answered 19/1, 2014 at 22:41 Comment(0)
S
10

All that's needed is a endpoint.publish() call above.

Stencil answered 27/1, 2014 at 15:49 Comment(1)
@HarmeetSingh I've not tried it but have you tried creating another method that returns an Endpoint?Stencil
T
12

Everything posted here is not 100% XML configuration free - all posts are using the classpath:META-INF/cxf/cxf.xml, which is also used in most tutorials on the web. But there´s a solution for that: Define a org.apache.cxf.bus.spring.SpringBus as @Bean and configure name = Bus.DEFAULT_BUS_ID, comming from org.apache.cxf.Bus.

As described in the other answers, the org.apache.cxf.jaxws.EndpointImpl has to be instantiated - including forwarding of the Beans SpringBus and the SEI-implementing Class. Also, the publish()-Method of EndpointImpl has to becalled, including a String containing an URL ending:

package de.jonashackt.tutorial.configuration;

import javax.xml.ws.Endpoint;

import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import de.codecentric.namespace.weatherservice.WeatherService;
import de.jonashackt.tutorial.endpoint.WeatherServiceEndpoint;

@Configuration
public class WebServiceConfiguration {

    @Bean
    public ServletRegistrationBean dispatcherServlet() {
        return new ServletRegistrationBean(new CXFServlet(), "/soap-api/*");
    }

    @Bean(name = Bus.DEFAULT_BUS_ID)
    public SpringBus springBus() {
        return new SpringBus();
    }    

    @Bean
    public WeatherService weatherService() {
        return new WeatherServiceEndpoint();
    }

    @Bean
    public Endpoint endpoint() {
        EndpointImpl endpoint = new EndpointImpl(springBus(), weatherService());
        endpoint.publish("/WeatherSoapService");
        return endpoint;
    }
}

If you want to learn more about Apache CXF together with SpringBoot, I recommend a look on this github project.

Transmissible answered 21/3, 2016 at 16:55 Comment(6)
It depends on what you mean by "XML free". The original post was about configuring a Java client without writing XML; no one said anything about not importing XML through Java config. Importing cxf.xml saves the client from having to write additional code, and I'd always prefer that in my code.Stencil
For me, "no XML" means, that you use Java Config starting from Spring 4.x (which includes Spring Boot) to define Beans. The cxf.xml defines Beans via Spring´s XML configuration. And by the way: importing the cxf.xml and declaring a Autowired Bean needs 3 lines - the same with the "no XML" approach :)Transmissible
How's @ImportResource({"classpath:META-INF/cxf/cxf.xml"}) 3 lines of code?Stencil
@AbhijitSarkar No JavaConfig means no xml... Defining Spring beans in cxf.xml clearly disqualifies the example.Dede
On a side note, if one is using Spring than the WebService probably uses autowiring/injection already, so maybe it's good idea to annotate/declare WeatherService with @Controller and inject it as a parameter in endpoint().Account
SpringBoot example. Not a Tomcat example.Infeudation
S
10

All that's needed is a endpoint.publish() call above.

Stencil answered 27/1, 2014 at 15:49 Comment(1)
@HarmeetSingh I've not tried it but have you tried creating another method that returns an Endpoint?Stencil
H
6

This thread definitely put me on the right track to getting CXF to run in pure Spring Java configuration, but it didn't provide everything that is required.

For my self, pure Java configuration means without a web.xml file, which I think this answer assumes is present. Spring Boot for example doesn't use a web.xml file.

So to register a CXF endpoint without the use of any XML files at all you will need a configuration file that also loads the CXFServlet.

import org.apache.cxf.Bus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

import javax.xml.ws.Endpoint;

@Configuration
@ImportResource({"classpath:META-INF/cxf/cxf.xml"})
public class JavaConfiguration {

    @Autowired
    private Bus bus;

    @Bean
    public Endpoint myServiceEndpoint() {
        EndpointImpl endpoint = new EndpointImpl(bus, new MyService());
        endpoint.publish("/myservice");
        return endpoint;
    }

    @Bean
    public ServletRegistrationBean cxfServlet() {
        ServletRegistrationBean servlet = new ServletRegistrationBean(new CXFServlet(), "/services/*");
        servlet.setLoadOnStartup(1);
        return servlet;
    }
}

The above is all the configuration required to successfully load a CXF endpoint within Spring.

I have also created a small project that demonstrates this.

Haydon answered 15/3, 2015 at 10:38 Comment(2)
You're right about registering the CXFServlet. I did register that but not in the same place as the CXF configuration and hence didn't show it in my answer. The Spring Java alternative for web.xml has nothing to do with CXF and if you need more configurations, you'll not be able to do like you did above. The Spring alternative for web.xml is AbstractAnnotationConfigDispatcherServletInitializer - here's how to do that.Stencil
But you are importing xml into your Java...how is that "pure Java"?Hopefully
G
1

I belive that if you pass your beans inside factory.setServiceBeans it will work

package br.com.app.spring;

import java.util.Arrays;

import javax.ws.rs.ext.RuntimeDelegate;

import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

import br.com.app.JaxRsApiApplication;
import br.com.app.services.EditionRest;
import br.com.app.services.EditionService;

@Configuration
@ImportResource(
    { 
        "classpath:META-INF/cxf/cxf.xml", 
        "classpath:META-INF/cxf/cxf-extension-xml.xml",
        "classpath:META-INF/cxf/cxf-servlet.xml" 
    })
public class CXFConfig {

@Bean(destroyMethod = "shutdown")
public SpringBus cxf() {
    return new SpringBus();
}

@Bean
public EditionService editionRest() {
    return new EditionRest();
}

@Bean
public JaxRsApiApplication jaxRsApiApplication() {
    return new JaxRsApiApplication();
}

@Autowired
@Bean
public Server jaxRsServer(JacksonJsonProvider provider) {

    JAXRSServerFactoryBean factory = RuntimeDelegate.getInstance().createEndpoint(jaxRsApiApplication(), JAXRSServerFactoryBean.class);
    factory.setServiceBeans(Arrays.<Object> asList(editionRest()));
    factory.setProviders(Arrays.<Object> asList(provider));

    return factory.create();
}

@Bean
public JacksonJsonProvider jsonProvider() {
    return new JacksonJsonProvider();
}
}
Gluck answered 30/10, 2014 at 17:49 Comment(0)
A
1

If you're using Spring Boot, you can use:

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
    <version>${cxf.version}</version> <!-- 3.1.7 or higher -->
</dependency>

To add an endpoint:

import javax.xml.ws.Endpoint;
import org.apache.cxf.Bus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WebServiceConfig {

    @Bean
    public Endpoint endpoint(Bus cxfBus) {
        EndpointImpl endpoint = new EndpointImpl(cxfBus, new Calculator());
        endpoint.publish("/CalculatorService");
        return endpoint;
    }
}

Official documentation of the CXF-Boot integration.

Amperage answered 25/12, 2017 at 8:12 Comment(1)
@Abhijit I disagree. There's nothing wrong with adding new answers to old questions if the solution did not exist when the question was asked. Also, the existence of cxf-spring-boot-starter-jaxws shows that people do use CXF with Boot.Amperage

© 2022 - 2024 — McMap. All rights reserved.