Category Archives: Web Service

Rest Template: Could Not Extract Response – No Suitable HttpMessageConverter Found for Response Type [X] and Content Type [application/json;charset=UTF-8]

PROBLEM

When invoking a web service using RestTemplate:-

restTemplate.getForObject("http://server/api", MyBean[].class));

… the following exception occurs:-

Exception in thread "main" org.springframework.web.client.RestClientException: 
Could not extract response: no suitable HttpMessageConverter found for response 
type [class [LMyBean;] and content type [application/json;charset=UTF-8]
	at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:110)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:572)
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:530)
	at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:237)

SOLUTION

If the content type is JSON, add the following dependency:-

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.6.0</version>
</dependency>

Spring Web Services: Client Integration Testing with MockWebServiceServer

Spring Web Services provides a great way to perform web service client integration tests. However, the example in the documentation requires the client class to extend org.springframework.ws.client.core.support.WebServiceGatewaySupport, which is rather ugly. Instead, I prefer to have WebServiceTemplate injected into my client class.

So, I made a slight tweak to my integration test to work this work…

Production Code To Be Tested

First, we need the following Spring Web Services dependency:

<dependency>
    <groupId>org.springframework.ws</groupId>
    <artifactId>spring-ws-core</artifactId>
    <version>2.1.4.RELEASE</version>
</dependency>

Let’s assume the client class looks like this:-

@Service
public class WSClientServiceImpl implements WSClientService {

    private WebServiceTemplate webServiceTemplate;

    @Autowired
    public WSClientServiceImpl(WebServiceTemplate webServiceTemplate) {
        this.webServiceTemplate = webServiceTemplate;
    }

    public String login(String username, String password) {
        Login login = new ObjectFactory().createLogin();
        login.setUsername(username);
        login.setPassword(password);

        SoapActionCallback callback = new SoapActionCallback("http://webservice/login");
        Object object = webServiceTemplate.marshalSendAndReceive(login, callback);

        JAXBElement<String> jaxbElement = (JAXBElement<String>) object;
        String sessionId = jaxbElement.getValue();

        return sessionId;
    }
}

The WebServiceTemplate bean is configured like this:-

<beans ...>
    <context:component-scan base-package="com.choonchernlim.epicapp"/>

    <bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
        <property name="contextPaths">
            <list>
                <value>...</value>
            </list>
        </property>
    </bean>

    <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
        <property name="marshaller" ref="marshaller"/>
        <property name="unmarshaller" ref="marshaller"/>
        <property name="defaultUri" value="http://webservice"/>
    </bean>
</beans>

Constructing Integration Test

Make sure we have the Spring Web Services Test framework dependency:-

<dependency>
    <groupId>org.springframework.ws</groupId>
    <artifactId>spring-ws-test</artifactId>
    <version>2.1.4.RELEASE</version>
    <scope>test</scope>
</dependency>

The integration test looks similar to the example in the Spring documentation, however, I injected WebServiceTemplate instead:-

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath*:applicationContext.xml"})
public class WSClientServiceImplTest {

    @Autowired
    private WSClientService wsClientService;

    @Autowired
    private WebServiceTemplate webServiceTemplate;

    private MockWebServiceServer mockServer;

    @Before
    public void createServer() throws Exception {
        mockServer = MockWebServiceServer.createServer(webServiceTemplate);
    }

    @Test
    public void testLogin() {
        Source requestPayload = new StringSource(
                "<ns2:Login xmlns:ns2='http://webservice/login'>" +
                "  <ns2:Username>user</ns2:Username>" +
                "  <ns2:Password>password</ns2:Password>" +
                "</ns2:Login>"
        );

        Source responsePayload = new StringSource(
                "<SessionID xmlns='http://webservice/login'>12345</SessionID>"
        );

        mockServer.expect(payload(requestPayload)).andRespond(withPayload(responsePayload));

        String sessionId = wsClientService.login("user", "password");

        assertThat(sessionId, is("12345"));

        mockServer.verify();
    }
}

Java: Invoking Secured Web Service with JSESSIONID

PROBLEM

I wrote a JSP custom tag that invokes a secured web service within the same application to perform some evaluation. This custom tag is only used in the secured views where the user has successfully authenticated against Spring Security, and they have access to these views. The secured web service is also guarded by Spring Security.

My custom tag implementation looks something like this:-

public int doStartTag() throws JspException {
	
	...
	
	HttpClient httpclient = HttpClientBuilder.create().build();
	
	HttpGet httpGet = new HttpGet(url);

	log.debug("Before executing HTTP Get...");

	HttpResponse response = httpclient.execute(httpGet);

	int statusCode = response.getStatusLine().getStatusCode();
	
	log.debug("Status Code: " + statusCode);

	return statusCode == HttpStatus.SC_OK ? EVAL_BODY_INCLUDE : SKIP_BODY;
}

The goal here is… if the response code from the secured web service is a 200 OK, then the custom tag will evaluate its body and include the content. Otherwise, it will hide it.

My first attempt is to test the custom tag with an invalid value so that the secured web service will throw a 400 Bad Request. The hope is the custom tag’s body will not get rendered in the view.

After getting authenticated successfully by Spring Security, the view containing this custom tag gets rendered. However, the response code always return a 200 OK even though I should get a 400 Bad Request from the secured web service.

After poking around, I decided to debug the console log… and this is what I see:-

[DEBUG] [CheckEditAccess] [hasAccess:72] - Before executing HTTP Get...
127.0.0.1 -  -  [29/Dec/2013:10:18:14 -0600] "GET /epic-app/api/request/invalid-key 
HTTP/1.1" 302 0 
127.0.0.1 -  -  [29/Dec/2013:10:18:14 -0600] "GET /epic-app/;jsessionid=1vlnl6eloabfh12r9x465kvuq 
HTTP/1.1" 200 6311 
[DEBUG] [CheckEditAccess] [hasAccess:75] - Status Code: 200

As you can see, there are two calls made even though my custom tag only makes one call. The first call is made by the custom tag but the response code is a 302 Found. Then, it gets directed to the next URL that returns a 200 OK.

Why is this happening?

SOLUTION

The problem here is because when the custom tag invokes the secured web service call, it is not done within the same session where the user is already authenticated by Spring Security. Thus, Spring Security prevents the actual call to go through. Instead, Spring Security redirects the request to the login page first (which is located at /epic-app in this case). As a result, the custom tag will receive a 200 OK from the login page instead of the response code from the secured web service.

To fix this, we need to send the session ID with the HTTP Get before executing it:-

public int doStartTag() throws JspException {
	
	...
	
	CookieStore cookieStore = new BasicCookieStore();
    BasicClientCookie cookie = new BasicClientCookie("JSESSIONID", pageContext.getSession().getId());
    cookieStore.addCookie(cookie);
	
	// very important to set the domain, otherwise this will not work!
    cookie.setDomain(request.getServerName());

    cookieStore.addCookie(cookie);

    HttpClient httpclient = HttpClientBuilder.create().setDefaultCookieStore(cookieStore).build();
	
	HttpGet httpGet = new HttpGet(url);

	log.debug("Before executing HTTP Get...");

	HttpResponse response = httpclient.execute(httpGet);

	int statusCode = response.getStatusLine().getStatusCode();
	
	log.debug("Status Code: " + statusCode);

	return statusCode == HttpStatus.SC_OK ? EVAL_BODY_INCLUDE : SKIP_BODY;
}

When I try it again, now I’m getting the intended 400 Bad Request from the secured web service due to input validation error.

[DEBUG] [CheckEditAccess] [hasAccess:72] - Before executing HTTP Get...
127.0.0.1 -  -  [29/Dec/2013:10:45:19 -0600] "GET /epic-app/api/request/invalid-key 
HTTP/1.1" 400 0 
[DEBUG] [CheckEditAccess] [hasAccess:73] - Status Code: 400

The message with Action ” cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher

PROBLEM

You get this error message when invoking a web service:-

org.springframework.ws.soap.client.SoapFaultClientException: The message with Action '' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).

SOLUTION

You forgot to specify the SOAP action before invoking the web service.

Open your WSDL file and search for the operation you are trying to invoke. You should see something like this:-

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions ...>
	...
    <wsdl:binding ...>
		...
        <wsdl:operation name="OhMyGawd">
            <soap:operation soapAction="http://oh.my.gawd"/>
			...
        </wsdl:operation>
    </wsdl:binding>
	...
</wsdl:definitions>

Take note of the soapAction value, in this example, it is http://oh.my.gawd.

If you are using Spring Web Services, add the following lines:-

@Autowired
private WebServiceTemplate webServiceTemplate;

public void run() {
	ObjectFactory objectFactory = new ObjectFactory();
    
	// Create the request payload
	OhMyGawd ohMyGawd = objectFactory.createOhMyGawd();
	ohMyGawd.setValue(...);

    OhMyGawdResponse response = (OhMyGawdResponse) webServiceTemplate.marshalSendAndReceive(
            ohMyGawd, 
			new SoapActionCallback("http://oh.my.gawd")
	);

	...
}

Cannot process the message because the content type ‘application/soap+xml; charset=utf-8’ was not the expected type ‘text/xml; charset=utf-8’

PROBLEM

You are getting this exception when invoking a web service:-

org.springframework.ws.client.WebServiceTransportException: Cannot process the message because the content type 'application/soap+xml; charset=utf-8' was not the expected type 'text/xml; charset=utf-8'. [415]

SOLUTION

You are using the wrong SOAP version. SOAP v1.1 uses text/xml while SOAP v1.2 uses application/soap+xml.

If you are using Spring Web Services, add the following lines:-

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util" xmlns:oxm="http://www.springframework.org/schema/oxm"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm.xsd">

	<!-- Configure the SOAP version to 1.1 -->
    <bean id="soapMessageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory">
        <property name="soapVersion">
            <util:constant static-field="org.springframework.ws.soap.SoapVersion.SOAP_11"/>
        </property>
    </bean>

    <oxm:jaxb2-marshaller id="marshaller" contextPath="myproject.wsdl.ws"/>

    <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
        <constructor-arg ref="soapMessageFactory"/>
        <property name="marshaller" ref="marshaller"/>
        <property name="unmarshaller" ref="marshaller"/>
        <property name="defaultUri" value="http://my.web.service?wsdl"/>
    </bean>
</beans>

Using Spring Web Services and JAXB to Invoke Web Service Based on WSDL

There are several ways to consume a web service based on a WSDL from Java. After trying a couple of approaches, I’m currently leaning towards Spring Web Services and JAXB. The biggest advantage of using both Spring Web Services and JAXB to consume a web service is the flexibility to change the web service URL without the need to regenerate the needed Java files, especially if you have a different web service URL for each environment (DEV, TEST and PROD).

In this tutorial, I’m going to consume this freely available web service that allows me to convert the currency rate from one country to another: http://www.webservicex.net/CurrencyConvertor.asmx?WSDL

currency.wsdl ( src/main/resources )

The first step is to open the WSDL link on the browser and save a copy of it. In this example, I’m saving it as currency.wsdl.

Your project structure should look something like this:-

myproject
|- src
|  |- main
|     |- java
|     |  |- myproject
|     |- resources
|        |- currency.wsdl
|- pom.xml

pom.xml

Add Spring Web Services dependency and configure JAXB-2 Maven plugin to generate the Java files based on the WSDL.

<project>
	...
	<dependencies>
	    <dependency>
	        <groupId>org.springframework.ws</groupId>
	        <artifactId>spring-ws-core</artifactId>
	        <version>2.1.2.RELEASE</version>
	    </dependency>
	</dependencies>

    <build>
        <plugins>
			...
			
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxb2-maven-plugin</artifactId>
                <version>1.5</version>
                <executions>
                    <execution>
                        <id>xjc</id>
                        <goals>
                            <goal>xjc</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
					<!-- Package to store the generated file -->
                    <packageName>myproject.wsdl.currency</packageName>
					<!-- Treat the input as WSDL -->
                    <wsdl>true</wsdl>
                    <!-- Input is not XML schema -->
					<xmlschema>false</xmlschema>
                    <!-- The WSDL file that you saved earlier -->
                    <schemaFiles>currency.wsdl</schemaFiles>
                    <!-- The location of the WSDL file -->
					<schemaDirectory>${project.basedir}/src/main/resources</schemaDirectory>
                    <!-- The output directory to store the generated Java files -->
					<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
                    <!-- Don't clear output directory on each run -->
                    <clearOutputDir>false</clearOutputDir>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Setting clearOutputDir to false is very important. If this is set to true and there’s a silly error in the JAXB-2 Maven plugin configuration, your output directory will be wiped clean. Since the output directory points to ${project.basedir}/src/main/java, we want to make sure we don’t lose all of our existing Java files.

Generating Java Files Based on WSDL

While you can run mvn clean jaxb2:xjc to generate the Java files, the easier way is to just run mvn clean compile. You should see a similar Maven output in your console:-

[INFO] ------------------------------------------------------------------------
[INFO] Building myproject 1.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- jaxb2-maven-plugin:1.5:xjc (xjc) @ myproject ---
[INFO] Generating source...
[INFO] parsing a schema...
[INFO] compiling a schema...
[INFO] myproject/wsdl/currency/ConversionRate.java
[INFO] myproject/wsdl/currency/ConversionRateResponse.java
[INFO] myproject/wsdl/currency/Currency.java
[INFO] myproject/wsdl/currency/ObjectFactory.java
[INFO] myproject/wsdl/currency/package-info.java
[INFO]

Your project structure should look something like this:-

myproject
|- src
|  |- main
|     |- java
|     |  |- myproject
|     |     |- wsdl
|     |        |- currency
|     |           |- ConversionRate.java
|     |           |- ConversionRateResponse.java
|     |           |- Currency.java
|     |           |- ObjectFactory.java
|     |           |- package-info.java
|     |- resources
|        |- currency.wsdl
|- pom.xml

applicationContext.xml ( src/main/resources )

Once we have the generated Java files, the next step is to configure Spring Web Services.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:oxm="http://www.springframework.org/schema/oxm" xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <context:component-scan base-package="myproject"/>

	<!-- Define the SOAP version used by the WSDL -->
    <bean id="soapMessageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory">
        <property name="soapVersion">
            <util:constant static-field="org.springframework.ws.soap.SoapVersion.SOAP_12"/>
        </property>
    </bean>

	<!-- The location of the generated Java files -->
    <oxm:jaxb2-marshaller id="marshaller" contextPath="myproject.wsdl.currency"/>

	<!-- Configure Spring Web Services -->
    <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
        <constructor-arg ref="soapMessageFactory"/>
        <property name="marshaller" ref="marshaller"/>
        <property name="unmarshaller" ref="marshaller"/>
        <property name="defaultUri" value="http://www.webservicex.net/CurrencyConvertor.asmx?WSDL"/>
    </bean>
</beans>

CurrentService.java ( src/main/java/myproject/service )

In the Service class, we will use Spring Web Services WebServiceTemplate to call the web service.

@Service
public class CurrencyService {
    @Autowired
    private WebServiceTemplate webServiceTemplate;

    public Double getConversionRate(Currency fromCurrency, Currency toCurrency) {
        ConversionRate conversionRate = new ObjectFactory().createConversionRate();
        conversionRate.setFromCurrency(fromCurrency);
        conversionRate.setToCurrency(toCurrency);

        ConversionRateResponse response = (ConversionRateResponse) webServiceTemplate.marshalSendAndReceive(
                conversionRate);

        return response.getConversionRateResult();
    }
}

Main.java ( src/main/java/myproject/main )

This is the main runner class.

public class Main {
    private static Logger log = Logger.getLogger(Main.class);

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        CurrencyService currencyService = context.getBean(CurrencyService.class);

        Currency fromCurrency = Currency.USD;
        Currency toCurrency = Currency.GBP;
        Double conversionRate = currencyService.getConversionRate(fromCurrency, toCurrency);

        log.info(String.format("The conversion rate from %s to %s is %s.", fromCurrency, toCurrency, conversionRate));
    }
}

Project Structure

Your project structure should look something like this:-

myproject
|- src
|  |- main
|     |- java
|     |  |- myproject
|     |     |- main
|     |        |- Main.java
|     |     |- service
|     |        |- CurrencyService.java
|     |     |- wsdl
|     |        |- currency
|     |           |- ConversionRate.java
|     |           |- ConversionRateResponse.java
|     |           |- Currency.java
|     |           |- ObjectFactory.java
|     |           |- package-info.java
|     |- resources
|        |- currency.wsdl
|- pom.xml

Result

When you run Main.java, you should see a result similar to this:-

The conversion rate from USD to GBP is 0.6178.

P/S: A shout-out to @jthiesse19 for helping me to write this shitty code… WAAAAAAA.