I'm relative new to the webservices world and my research seems to have confused me more than enlighten me, my problem is that I was given a library(jar) which I have to extend with some webservice functionality.
This library will be shared to other developers, and among the classes in the jar will be classes that have a method which calls a webservice (that essentially sets an attribute of the class, does some business logic, like storing the object in a db, etc and sends back the object with those modifications). I want to make the call to this service as simple as possible, hopefully as simple so that the developer using the class only need to do.
Car c = new Car("Blue");
c.webmethod();
I have been studying JAX-WS to use on the server but seems to me that I don't need to create a wsimport in the server nor the wsimport on the client, since I know that both have the classes, I just need some interaction between classes shared in both the server and the client. How do you think makes sense to do the webservice and the call in the class?
I understand your problem boils down to how to call a SOAP (JAX-WS) web service from Java and get its returning object. In that case, you have two possible approaches:
Generate the Java classes through wsimport and use them; or
Create a SOAP client that:
Serializes the service's parameters to XML;
Calls the web method through HTTP manipulation; and
Parse the returning XML response back into an object.
About the first approach (using wsimport):
I see you already have the services' (entities or other) business classes, and it's a fact that the wsimport generates a whole new set of classes (that are somehow duplicates of the classes you already have).
I'm afraid, though, in this scenario, you can only either:
Adapt (edit) the wsimport generated code to make it use your business classes (this is difficult and somehow not worth it - bear in mind everytime the WSDL changes, you'll have to regenerate and readapt the code); or
Give up and use the wsimport generated classes. (In this solution, you business code could "use" the generated classes as a service from another architectural layer.)
About the second approach (create your custom SOAP client):
In order to implement the second approach, you'll have to:
Make the call:
Use the SAAJ (SOAP with Attachments API for Java) framework (see below, it's shipped with Java SE 1.6 or above) to make the calls; or
You can also do it through java.net.HttpUrlconnection (and some java.io handling).
Turn the objects into and back from XML:
Use an OXM (Object to XML Mapping) framework such as JAXB to serialize/deserialize the XML from/into objects
Or, if you must, manually create/parse the XML (this can be the best solution if the received object is only a little bit differente from the sent one).
Creating a SOAP client using classic java.net.HttpUrlConnection is not that hard (but not that simple either), and you can find in this link a very good starting code.
I recommend you use the SAAJ framework:
SOAP with Attachments API for Java (SAAJ) is mainly used for dealing directly with SOAP Request/Response messages which happens behind the scenes in any Web Service API. It allows the developers to directly send and receive soap messages instead of using JAX-WS.
See below a working example (run it!) of a SOAP web service call using SAAJ. It calls this web service.
import javax.xml.soap.*;
public class SOAPClientSAAJ {
// SAAJ - SOAP Client Testing
public static void main(String args[]) {
/*
The example below requests from the Web Service at:
https://www.w3schools.com/xml/tempconvert.asmx?op=CelsiusToFahrenheit
To call other WS, change the parameters below, which are:
- the SOAP Endpoint URL (that is, where the service is responding from)
- the SOAP Action
Also change the contents of the method createSoapEnvelope() in this class. It constructs
the inner part of the SOAP envelope that is actually sent.
*/
String soapEndpointUrl = "https://www.w3schools.com/xml/tempconvert.asmx";
String soapAction = "https://www.w3schools.com/xml/CelsiusToFahrenheit";
callSoapWebService(soapEndpointUrl, soapAction);
}
private static void createSoapEnvelope(SOAPMessage soapMessage) throws SOAPException {
SOAPPart soapPart = soapMessage.getSOAPPart();
String myNamespace = "myNamespace";
String myNamespaceURI = "https://www.w3schools.com/xml/";
// SOAP Envelope
SOAPEnvelope envelope = soapPart.getEnvelope();
envelope.addNamespaceDeclaration(myNamespace, myNamespaceURI);
/*
Constructed SOAP Request Message:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:myNamespace="https://www.w3schools.com/xml/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<myNamespace:CelsiusToFahrenheit>
<myNamespace:Celsius>100</myNamespace:Celsius>
</myNamespace:CelsiusToFahrenheit>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
*/
// SOAP Body
SOAPBody soapBody = envelope.getBody();
SOAPElement soapBodyElem = soapBody.addChildElement("CelsiusToFahrenheit", myNamespace);
SOAPElement soapBodyElem1 = soapBodyElem.addChildElement("Celsius", myNamespace);
soapBodyElem1.addTextNode("100");
}
private static void callSoapWebService(String soapEndpointUrl, String soapAction) {
try {
// Create SOAP Connection
SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
SOAPConnection soapConnection = soapConnectionFactory.createConnection();
// Send SOAP Message to SOAP Server
SOAPMessage soapResponse = soapConnection.call(createSOAPRequest(soapAction), soapEndpointUrl);
// Print the SOAP Response
System.out.println("Response SOAP Message:");
soapResponse.writeTo(System.out);
System.out.println();
soapConnection.close();
} catch (Exception e) {
System.err.println("\nError occurred while sending SOAP Request to Server!\nMake sure you have the correct endpoint URL and SOAPAction!\n");
e.printStackTrace();
}
}
private static SOAPMessage createSOAPRequest(String soapAction) throws Exception {
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage soapMessage = messageFactory.createMessage();
createSoapEnvelope(soapMessage);
MimeHeaders headers = soapMessage.getMimeHeaders();
headers.addHeader("SOAPAction", soapAction);
soapMessage.saveChanges();
/* Print the request message, just for debugging purposes */
System.out.println("Request SOAP Message:");
soapMessage.writeTo(System.out);
System.out.println("\n");
return soapMessage;
}
}
About using JAXB for serializing/deserializing, it is very easy to find information about it. You can start here: http://www.mkyong.com/java/jaxb-hello-world-example/.
Or just use Apache CXF's wsdl2java to generate objects you can use.
It is included in the binary package you can download from their website. You can simply run a command like this:
$ ./wsdl2java -p com.mynamespace.for.the.api.objects -autoNameResolution http://www.someurl.com/DefaultWebService?wsdl
It uses the wsdl to generate objects, which you can use like this (object names are also grabbed from the wsdl, so yours will be different a little):
DefaultWebService defaultWebService = new DefaultWebService();
String res = defaultWebService.getDefaultWebServiceHttpSoap11Endpoint().login("webservice","dadsadasdasd");
System.out.println(res);
There is even a Maven plug-in which generates the sources: https://cxf.apache.org/docs/maven-cxf-codegen-plugin-wsdl-to-java.html
Note: If you generate sources using CXF and IDEA, you might want to look at this: https://stackoverflow.com/a/46812593/840315
Might help for someone who have xml request as string.
if you have WSDL, You can create a new soap request in SoapUI with that WSDL file.
It would automatically generate the Structure/XML for input request.
Here is some simple version of Java code you can use to call Soap service if you have the input request xml from SoapUI:
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class SimpleSoapClient {
public static void main(String args[]) throws IOException {
String address="Hyderabad";
/* place your xml request from soap ui below with necessary changes in parameters*/
String xml="<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ws=\"http://www.YourUrlAsPerWsdl.com/\">\r\n" +
" <soapenv:Header/>\r\n" +
" <soapenv:Body>\r\n" +
" <ws:callRest>\r\n" +
" <name>"+"Hello"+"</name>\r\n" +
" <address>"+address+"</address>\r\n" +
" </ws:callRest>\r\n" +
" </soapenv:Body>\r\n" +
"</soapenv:Envelope>";
String responseF=callSoapService(xml);
System.out.println(responseF);
}
}
static String callSoapService(String soapRequest) {
try {
String url = "https://gogle.com/service/hello"; // replace your URL here
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
// change these values as per soapui request on top left of request, click on RAW, you will find all the headers
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type","text/xml; charset=utf-8");
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.writeBytes(soapRequest);
wr.flush();
wr.close();
String responseStatus = con.getResponseMessage();
System.out.println(responseStatus);
BufferedReader in = new BufferedReader(new InputStreamReader(
con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
// You can play with response which is available as string now:
String finalvalue= response.toString();
// or you can parse/substring the required tag from response as below based your response code
finalvalue= finalvalue.substring(finalvalue.indexOf("<response>")+10,finalvalue.indexOf("</response>")); */
return finalvalue;
}
catch (Exception e) {
return e.getMessage();
}
}
}
I found a much simpler alternative way to generating soap message.
Given a Person Object:
import com.fasterxml.jackson.annotation.JsonInclude;
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Person {
private String name;
private int age;
private String address; //setter and getters below
}
Below is a simple Soap Message Generator:
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
#Slf4j
public class SoapGenerator {
protected static final ObjectMapper XML_MAPPER = new XmlMapper()
.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.registerModule(new JavaTimeModule());
private static final String SOAP_BODY_OPEN = "<soap:Body>";
private static final String SOAP_BODY_CLOSE = "</soap:Body>";
private static final String SOAP_ENVELOPE_OPEN = "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">";
private static final String SOAP_ENVELOPE_CLOSE = "</soap:Envelope>";
public static String soapWrap(String xml) {
return SOAP_ENVELOPE_OPEN + SOAP_BODY_OPEN + xml + SOAP_BODY_CLOSE + SOAP_ENVELOPE_CLOSE;
}
public static String soapUnwrap(String xml) {
return StringUtils.substringBetween(xml, SOAP_BODY_OPEN, SOAP_BODY_CLOSE);
}
}
You can use by:
public static void main(String[] args) throws Exception{
Person p = new Person();
p.setName("Test");
p.setAge(12);
String xml = SoapGenerator.soapWrap(XML_MAPPER.writeValueAsString(p));
log.info("Generated String");
log.info(xml);
}
Related
I need to call Oauth2 ResT API service to fetch the access token and expire_in values from the JSON file by it.
Below is a sample CURL which i need to call using JAVA i am beginner in JAVA so not able to figure out how to do it however i can do it using shell script.
curl -u 'ClientId:Clientaccesskey' https://oauth2.url/oauth/token -X POST -d 'response_type=token&client_id=ClientId&username=user&password=userpassword&scope=process&grant_type=password'
Sample JSON retured by above curl command --
{"access_token":"accessTokentobefetched","token_type":"bearer","refresh_token":"refreshToken","expires_in":7199,"scope":"process","jti":"somehexadecimalvaliu"}
In shell script we can fetch the value of access token and other fields using AWK command and other commands.
So i need to call this CURL command in JAVA and fetch the value of access token and other keys from the JSON file.
Any help which can help me start with this is welcome as i am new to JAVA and learning.
There are quite a few libraries that you can use to help you make a regular HTTP POST request from Java, but since you seem to require to send plain text/plain body content - I suggest that you use okhttp3. This is a fairly lightweight and easy to work with HTTP client.
You will need to add the following dependency to your pom.xml, grabbed from https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp/4.7.2:
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.7.2</version>
</dependency>
If you are using gradle, just visit the before mentioned URL, and get the gradle equivalent dependency declaration.
And here's a complete class that illustrates how the okhttp3 client can be used to perform the POST request, and extract the return value. This example expects that you are using the spring-boot-starter-web dependency (this will include the jackson and tomcat libraries that are used in the example).
package com.example.demo;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
#Component
public class TokenRequester {
public String getAccessToken() throws IOException {
// Create a new HTTP client
OkHttpClient client = new OkHttpClient().newBuilder().build();
// Create the request body
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "response_type=token&client_id=ClientId&username=user&password=userpassword&scope=process&grant_type=password");
// Build the request object, with method, headers
Request request = new Request.Builder()
.url("https://oauth2.url/oauth/token")
.method("POST", body)
.addHeader("Authorization", createAuthHeaderString("ClientId", "Clientaccesskey"))
.addHeader("Content-Type", "text/plain")
.build();
// Perform the request, this potentially throws an IOException
Response response = client.newCall(request).execute();
// Read the body of the response into a hashmap
Map<String,Object> responseMap = new ObjectMapper().
readValue(response.body().byteStream(), HashMap.class);
// Read the value of the "access_token" key from the hashmap
String accessToken = (String)responseMap.get("access_token");
// Return the access_token value
return accessToken;
}
// Just a helper metod to create the basic auth header
private String createAuthHeaderString(String username, String password) {
String auth = username + ":" + password;
byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(StandardCharsets.US_ASCII));
String authHeader = "Basic " + new String(encodedAuth);
return authHeader;
}
}
You may need to tweak a few things here. I could ask you to supply me the verbose output from the curl command, in order to be sure about the encoding - but give this one a try and see what you get?
Here's a solution that involves only Spring, using a RestTemplate for the POST request.
I found that when you use curl -X POST -d 'key=data', curl will add the header content-type: application/x-www-form-urlencoded, so the solution here will do the same.
This solution sets up the RestTemplate with the headers and body you have specified, and captures the response in an object equivalent to the one you have described.
The following solution consists of two files that you can try to introduce into your solution:
RestTemplateTokenRequester.java
package com.example.demo;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
#Component
public class RestTemplateTokenRequester {
public TokenResponse requestAccessToken() {
// Create a RestTemplate to describe the request
RestTemplate restTemplate = new RestTemplate();
// Specify the http headers that we want to attach to the request
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.add("Authorization", createAuthHeaderString("ClientId", "Clientaccesskey"));
// Create a map of the key/value pairs that we want to supply in the body of the request
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("response_type","token");
map.add("client_id","ClientId");
map.add("username","user");
map.add("password","userpassword");
map.add("scope","process");
map.add("grant_type","password");
// Create an HttpEntity object, wrapping the body and headers of the request
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);
// Execute the request, as a POSt, and expecting a TokenResponse object in return
ResponseEntity<TokenResponse> response =
restTemplate.exchange("https://oauth2.url/oauth/token",
HttpMethod.POST,
entity,
TokenResponse.class);
return response.getBody();
}
// Just a helper metod to create the basic auth header
private String createAuthHeaderString(String username, String password) {
String auth = username + ":" + password;
byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(StandardCharsets.US_ASCII));
String authHeader = "Basic " + new String(encodedAuth);
return authHeader;
}
}
TokenResponse.java
This is simply a POJO that is used by the jackson mapper, to capture the response in an object that you can easily read your result from.
package com.example.demo;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonIgnoreProperties(ignoreUnknown = true)
public class TokenResponse {
#JsonProperty("access_token")
private String accessToken;
#JsonProperty("token_type")
private String tokenType;
#JsonProperty("refresh_token")
private String refreshToken;
#JsonProperty("expires_in")
private Integer expiresIn;
#JsonProperty("scope")
private String scope;
#JsonProperty("jti")
private String jti;
}
I hope this solution will help you - I would prefer it over the other solution I have suggested with okhttp3.
curl is a HTTP client.
better solution is using HTTP client APIs for java to call endpoints.
RestTemplate is common HTTP client comes with spring and it is your best choice.
I have been trying to use restful webservice in place of legacy soap webservice without altering the user part of the request. I wanted to know whether this is achievable. Here is a sample code to demonstrate the issue :
Soap request :
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sch="https://www.flopradalley.com/xml/school">
<soapenv:Header/>
<soapenv:Body>
<sch:StudentDetailsRequest>
<sch:name>Arun</sch:name>
</sch:StudentDetailsRequest>
</soapenv:Body>
</soapenv:Envelope>
And this is my rest controller which should be able to respond to the request from SoapUI :
#RestController
#RequestMapping(value = "/restservice")
public class KenRestController {
#RequestMapping(value = "/details", method = RequestMethod.POST,
produces = MediaType.TEXT_XML_VALUE, consumes = MediaType.TEXT_XML_VALUE
)
public StudentDetailsResponse execute(HttpServletRequest request)
{
StudentDetailsRequest objectFromBody = null;// unmarshall the object from soap request and store it here
StringBuffer sb = new StringBuffer();
try {
BufferedReader reader = request.getReader();
String lineStr = null;
while ((lineStr = reader.readLine()) != null) {
sb.append(lineStr);
}catch(IOException ex) {
ex.printStackTrace();
}
/* other code */
return ---;
}
}
I was only able to get the soap request as text and store it in a string buffer. I know this isn't remotely the way it should be done if it is possible to handle soap requests using rest. I know that rest is an archicture and is a whole lot difference from soap and it looks like there is no direct way to handle soap requests using rest at the least.
Apart from this sample code, I should be able to extract the SoapMessage, MessageContext, Soapheader, SoapBody from the request. That brings me to the original question on whether it is possible?
If you make the change into a REST API I highly recommend to not keep thinging in some of the webservice logic.
The SOAP protocol is a method to pass information that contains both the answer and the metadata of the call.
In rest you obtain the answer in the body of the responses and the most used format is json. You can directly pass as a parameter in the method the object that is going to be parsed into the java object by Spring REST (it uses Jackson library to parse in reality).
#RestController
public class KenRestController {
#GetMapping("/restService/details/{studentId}") // post is for saving an elelement
public StudentDetailsResponse getDetails(#PathVariable("studentId") Integer studentId) {
// you get the details and parse it to the object you want to return
return studentService.getStudentById(studentId);
}
// example of response class
private class StudentDetailsResponse implements Serializable{
// pojo class
}
}
Please don't hesitate to make any questions about that.
I am using a jax-ws Handler to intercept a SOAP message so that I can grab certain elements from the Header. Is there a way to deserialize a Header and parse it into either wsdl2java or xmlbeans generated object? I am currently migrating from AXIS to CXF. Under AXIS, I could get the Header xml and parse it using a Factory class. I have not found a good way to do this with the objects generated from CXF wsdl2java. I realize that I can use the getElementsByTagName to retrieve each Node, but it is just easier to work with the actual object.
You can use SAAJ API to manipulate the SOAP message directly. Extend AbstractSoapInterceptor by implementing the handleMessage() method and add it to the chain of InInterceptors.
public class MyCustomInInterceptor extends AbstractSoapInterceptor {
public void handleMessage(SoapMessage soapMessage) throws Fault {
try {
SOAPMessage message = soapMessage.getContent(SOAPMessage.class);
SOAPPart sp = message.getSOAPPart();
SOAPEnvelope se = sp.getEnvelope();
SOAPBody sb = se.getBody();
SOAPHeader sh = se.getHeader();
}
catch(Exception e)
{
log.error(e);
throw new Fault(e);
}
}
}
NOTE: You will also need to attach SAAJInInterceptor to the chain of interceptors or else soapMessage.getContent(SOAPMessage.class) will return null
I have a code but it is printing only the status, what i want is my code to prnt the entire code "Response" on console.
import java.io.IOException;
import java.net.URL;
import java.net.HttpURLConnection;
public class API{
public static void main(String args[]) throws IOException
{
URL url = new URL("http://testpath");
HttpURLConnection http = (HttpURLConnection)url.openConnection();
int statusCode = http.getResponseCode();
System.out.println(statusCode);
}
}
Your class is named API yet you seem to not have carefully read the API for HttpURLConnection. :[
getResponseMessage() is probably what you want.
If you want access to certain headers you will need to use the properties defined in URLConnection
The following methods are used to access the header fields and the contents after the connection is made to the remote object:
getContent
getHeaderField
getInputStream
getOutputStream
Certain header fields are accessed frequently. The methods:
getContentEncoding
getContentLength
getContentType
getDate
getExpiration
getLastModifed
This must work..
String statusCode = http.getResponseMessage();
System.out.println(statusCode);
Use getResponseMessage() method from HttpURLConnection to get completer response message
I have a spring-ws (2.0.2) service I have implemented that requires some custom elements in the soap header. I am trying to use Spring's MockWebServiceClient to generate a valid request to test the dispatcher, marshallers, etc.
The problem I am getting is that the MockWebSerivce only seems to support the Soap Body (the payload).
How can I access the soap request being generated to get the right headers into it?
If there is a better library for doing this other than Spring's MockWebServiceClient, that would be fine too.
Related Links:
http://forum.springsource.org/showthread.php?101708-MockWebServiceClient-amp-WS-Security
Add SoapHeader to org.springframework.ws.WebServiceMessage
I had the similar problem when I wanted to test spring web service with Security, I ended up using the Spring Interceptors to modify the header before they reach end point, I enabled the interceptors only for testing.
Create an interceptor, I implemented the SmartEndpointInterceptor, You can use the other interceptors if you choose
public class ModifySoapHeaderInterceptor implements
SmartEndpointInterceptor
{
//WSConstants.WSSE_NS;
private static final String DEFAULT_SECURITY_URL = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
private static final String SECURITY_TAG = "Security";
private static final String SECURITY_PREFIX = "wsse";
private static final String USER_NAME_TOKEN = "UsernameToken";
#Override
public boolean handleRequest(MessageContext messageContext, Object endpoint)
throws Exception
{
SaajSoapMessage saajSoapMessage(SaajSoapMessage)messageContext.getRequest());
SOAPHeader soapHeader = saajSoapMessage.getSaajMessage().getSOAPPart()
.getEnvelope().getHeader();
//you can modify header's as you choose
Name headerElement = saajSoapMessage.getSaajMessage().getSOAPPart()
.getEnvelope()
.createName(SECURITY_TAG, SECURITY_PREFIX, DEFAULT_SECURITY_URL);
SOAPHeaderElement soapHeaderElement = soapHeader
.addHeaderElement(headerElement);
SOAPElement usernameToken = soapHeaderElement.addChildElement(
USER_NAME_TOKEN, SECURITY_PREFIX);
SOAPElement userNameElement = usernameToken.addChildElement("Username",
SECURITY_PREFIX);
userNameElement.addTextNode("userid");//you can inject via spring
SOAPElement passwordElement = usernameToken.addChildElement("Password",
SECURITY_PREFIX);
passwordElement.addTextNode("password");
return true;
}
}
Configure this interceptor in spring context
<sws:interceptors>
<bean class="prasanna.ws.security.wss4j.ModifySoapHeaderInterceptor"/>
</sws:interceptors>
This will add the necessary security headers to the message before it reaches the end point, You can still use MockWebServiceClient to test your web service.
As you noted, the MockWebServiceClient sendRequest() method only sets up the SOAP body with the payload given it. It does not touch the SOAP header.
To set up the SOAP header as well you can create a class that implements the RequestCreator interface and sets the SOAP header. Pass an instance of this class to the sendRequest() method.
For example:
class SoapActionCreator implements RequestCreator {
private final Source payload;
public SoapActionCreator(Source payload) {
this.payload = payload;
}
#Override
public WebServiceMessage createRequest(WebServiceMessageFactory webServiceMessageFactory)
throws IOException {
WebServiceMessage webServiceMessage =
new PayloadMessageCreator(payload).createMessage(webServiceMessageFactory);
SoapMessage soapMessage = (SoapMessage) webServiceMessage;
SoapHeader header = soapMessage.getSoapHeader();
// Add an Action element to the SOAP header
StringSource headerSource = new StringSource(
"<wsa:Action xmlns:wsa=\"http://www.w3.org/2005/08/addressing\">https://example.com/foo/bar</wsa:Action>");
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(headerSource, header.getResult());
return webServiceMessage;
}
}
Then use SoapActionCreator like this:
SoapActionCreator soapActionCreator = new SoapActionCreator(requestPayload);
mockClient.sendRequest(soapActionCreator).
andExpect(payload(responsePayload));
where requestPayload is the SOAP request body and responsePayload is the entire SOAP response (header and body).
I did find the smock library which does what I really wanted: just takes a text file with the whole request in it.
http://code.google.com/p/smock/wiki/SpringWs
It supports the same request and response matchers as the spring provided stuff. It also keeps my tests very self contained. (Rather than a whole new class that would only be used in my test cases.)