I have a Spring boot SOAP services with cxf, and my Consumers are passing me SSO token in http header.. I am able to retrieve the SSO token using JAX-WS handler. I am saving that SSO token into handler class level variable, and after control going through various classes it reaches to a point where I have to make a request to another service and have to pass the same SSO token, but in my Connection class the SSO token value is NULL.
#Component
public class EndPointHandler implements SOAPHandler<SOAPMessageContext> {
private List<String> ssoToken;
private Map<String, List<String>> headers;
#Override
public boolean handleMessage(SOAPMessageContext context) {
Boolean isResponse = (Boolean) context.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (!isResponse) {
headers = ((Map<String, List<String>>) context.get(MessageContext.HTTP_REQUEST_HEADERS));
if (headers != null) {
if (!headers.get("SSOToken").get(0).isEmpty()) {
List<String> ssoToken = headers.get("SSOToken");
LOGGER.info(ssoToken.get(0));
this.ssoToken = ssoToken;
} else {
LOGGER.error("SSO Token value cannot be empty");
return false;
}
}
}
return true;
}
public void setSSOToken() {
headers.put("SSOToken", this.ssoToken);
}
}
In my Connection class I have to set this SSO token as a header and make a call to another service but SSO token value is NULL.
Connection Class:
#Component
public class ConnectionManager {
private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionManager.class);
#Autowired
private EndPointHandler handler;
private void establishConnection(String uri) throws FileNetIntegrationException {
handler.ssoToken; // --> I need SSO token here but the value is NULL;
}
}
This is how I set the handler chain in my WebServiceConfig class:
#Bean
public Endpoint endpoint(Bus bus) {
EndpointImpl endpoint = new EndpointImpl(bus, changeServiceEndpoint);
WebService ws = AnnotationUtils.findAnnotation(endpoint.getImplementorClass(), WebService.class);
endpoint.setAddress("/" + ws.serviceName());
endpoint.publish();
SOAPBinding binding = (SOAPBinding) endpoint.getBinding();
ArrayList<Handler> handlerChain = new ArrayList<>();
handlerChain.add(new EndPointHandler());
binding.setHandlerChain(handlerChain);
return endpoint;
}
I think, I got a solution right after posting the last piece of code here when I noticed that I used new EndPointHandler() while adding it into handler chain.. I tried using Autowired it and it worked for me.
As far as I know, jax-ws context and spring-ws context don't intersect with each other. So this is not a solution but a workaround. As another workaround, you can use some singleton synchronizedMap, or use a jax-ws Handler to redirect requests to another endpoint with extended api.
Access HTTP headers of SOAP messages using JAX-WS handler
If you have configured SOAPHandler for your JAX-WS WebService, then you can access the HTTP headers and pass them on inside the SOAPBody as fields of the SOAPElement type, and vice versa. To do this you have to extend the messages POJOs with these fields annotated as XmlElement. Then you can access them from your WebService.
Note: If you have a dynamically generated WSDL schema based on Java classes, then it changes too. But anyway, the old requests and responses are accepted, because these fields are not required by default.
Example: UserService - the incoming message has one field of String type and outgoing message has one field of int type. Let's extend each of them with one ssoToken field of String type. We'll read this token from HTTP headers of incoming message and send it back in HTTP headers of outgoing message.
GetUserRequest.java:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType
#XmlRootElement(name = "getUserRequest")
public class GetUserRequest {
#XmlElement
protected String ssoToken;
#XmlElement(required = true)
protected String name;
// getters + setters + constructor
}
GetUserResponse.java:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType
#XmlRootElement(name = "getUserResponse")
public class GetUserResponse {
#XmlElement
protected String ssoToken;
#XmlElement(required = true)
protected int age;
// getters + setters + constructor
}
UserServiceHandler.java
public class UserServiceHandler implements SOAPHandler<SOAPMessageContext> {
#Override
public boolean handleMessage(SOAPMessageContext soapMessageContext) {
Boolean isResponse =
(Boolean) soapMessageContext
.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (!isResponse) {
// Request message
return processIncomingMessage(soapMessageContext);
} else {
// Response message
return processOutgoingMessage(soapMessageContext);
}
}
}
processIncomingMessage:
#SuppressWarnings("unchecked")
private boolean processIncomingMessage(SOAPMessageContext soapMessageContext) {
Map<String, List<String>> headers =
((Map<String, List<String>>) soapMessageContext
.get(MessageContext.HTTP_REQUEST_HEADERS));
if (headers == null || headers.isEmpty()) {
return false;
}
List<String> ssoTokens = headers.get("SSOToken");
if (ssoTokens == null || ssoTokens.size() != 1) {
return false;
}
String ssoToken = ssoTokens.get(0);
if (ssoToken == null || ssoToken.isEmpty()) {
return false;
}
try {
Iterator<Node> iterator =
soapMessageContext.getMessage().getSOAPBody().getChildElements();
while (iterator.hasNext()) {
Node element = iterator.next();
if (element.getNodeName().contains("getUserRequest")) {
((SOAPElement) element)
.addChildElement("ssoToken", element.getPrefix())
.setTextContent(ssoToken);
}
}
} catch (SOAPException e) {
e.printStackTrace();
return false;
}
return true;
}
processOutgoingMessage:
#SuppressWarnings("unchecked")
private boolean processOutgoingMessage(SOAPMessageContext soapMessageContext) {
Map<String, List<String>> headers =
((Map<String, List<String>>) soapMessageContext
.get(MessageContext.HTTP_RESPONSE_HEADERS));
if (headers == null || headers.isEmpty()) {
soapMessageContext
.put(MessageContext.HTTP_RESPONSE_HEADERS, new HashMap<>());
headers = ((Map<String, List<String>>) soapMessageContext
.get(MessageContext.HTTP_RESPONSE_HEADERS));
}
try {
Iterator<Node> iterator = soapMessageContext
.getMessage().getSOAPBody().getChildElements();
while (iterator.hasNext()) {
Node element = iterator.next();
if (element.getNodeName().contains("getUserResponse")) {
Iterator<Node> iteratorResponse =
((SOAPElement) element).getChildElements();
while (iteratorResponse.hasNext()) {
Node childElement = iteratorResponse.next();
if (childElement.getNodeName().contains("ssoToken")) {
String ssoToken = childElement.getTextContent();
headers.put("SSOToken",
Collections.singletonList(ssoToken));
element.removeChild(childElement);
}
}
}
}
} catch (SOAPException e) {
e.printStackTrace();
return false;
}
return true;
}
UserService.java
#WebService(endpointInterface = "com.example.UserPort",
serviceName = "UserService")
#HandlerChain(file="handler-chain.xml")
public class UserService implements UserPort {
public GetUserResponse getUser(GetUserRequest request) {
GetUserResponse response = new GetUserResponse();
response.setAge(23);
response.setSsoToken(request.getSsoToken());
return response;
}
}
Testing with SoapUI:
Request headers:
POST http://localhost:8080/ws/userService HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: ""
SSOToken: 6cd506ac-738a-43ca-aee8-d13b78180605
Content-Length: 296
Host: localhost:8080
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
Request message:
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:spr="http://example.com/jax-ws-sample">
<soapenv:Header/>
<soapenv:Body>
<spr:getUserRequest>
<spr:name>John</spr:name>
</spr:getUserRequest>
</soapenv:Body>
</soapenv:Envelope>
Response headers:
HTTP/1.1 200
SSOToken: 6cd506ac-738a-43ca-aee8-d13b78180605
Content-Type: text/xml;charset=utf-8
Transfer-Encoding: chunked
Date: Thu, 27 Aug 2020 15:54:33 GMT
Keep-Alive: timeout=20
Connection: keep-alive
Response message:
<S:Envelope
xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<S:Body>
<getUserResponse xmlns="http://example.com/jax-ws-sample">
<age>23</age>
</getUserResponse>
</S:Body>
</S:Envelope>
Related
So i have below scenario to implement using Spring boot rest template to consume a REST-API (involves token authentication mechanism). To perform test i've created simple mock REST API in spring boot. Here's the process,
From my API consumer app,
sends a request using rest-template to consume a protected API, this API requires Authorization: Bearer <token> header to be present in request.
if something is wrong with this token (missing header, invalid token), protected API returns HTTP-Unauthorized (401).
when this happens, consumer API should send another request to another protected API that returns a valid access token, this protected API requires Authorization: Basic <token> header to be present. New access token will be stored in a static field and it will be used in all other requests to authenticate.
This can be achieved by simply catching 401-HttpClientErrorException in RestTemplate consumer methods (postForObject), but the idea was to decouple it from REST-API consumer classes. To achieve it, i tried to use ClientHttpRequestInterceptor
Here's the code, that i tried so far.
Interceptor class
public class AuthRequestInterceptor implements ClientHttpRequestInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(AuthRequestInterceptor.class);
private static final String BASIC_AUTH_HEADER_PREFIX = "Basic ";
private static final String BEARER_AUTH_HEADER_PREFIX = "Bearer ";
//stores access token
private static String accessToken = null;
#Value("${app.mife.apiKey}")
private String apiKey;
#Autowired
private GenericResourceIntegration resourceIntegration; // contains methods of rest template
#Override
public ClientHttpResponse intercept(
HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution
) throws IOException {
LOGGER.info("ReqOn|URI:[{}]{}, Headers|{}, Body|{}", request.getMethod(), request.getURI(), request.getHeaders(), new String(body));
request.getHeaders().add(ACCEPT, APPLICATION_JSON_VALUE);
request.getHeaders().add(CONTENT_TYPE, APPLICATION_JSON_VALUE);
try {
//URI is a token generate URI, request
if (isBasicUri(request)) {
request.getHeaders().remove(AUTHORIZATION);
//sets BASIC auth header
request.getHeaders().add(AUTHORIZATION, (BASIC_AUTH_HEADER_PREFIX + apiKey));
ClientHttpResponse res = execution.execute(request, body);
LOGGER.info("ClientResponse:[{}], status|{}", "BASIC", res.getStatusCode());
return res;
}
//BEARER URI, protected API access
ClientHttpResponse response = null;
request.getHeaders().add(AUTHORIZATION, BEARER_AUTH_HEADER_PREFIX + getAccessToken());
response = execution.execute(request, body);
LOGGER.info("ClientResponse:[{}], status|{}", "BEARER", response.getStatusCode());
if (unauthorized(response)) {
LOGGER.info("GetToken Res|{}", response.getStatusCode());
String newAccessToken = generateNewAccessCode();
request.getHeaders().remove(AUTHORIZATION);
request.getHeaders().add(AUTHORIZATION, (BEARER_AUTH_HEADER_PREFIX + newAccessToken));
LOGGER.info("NewToken|{}", newAccessToken);
return execution.execute(request, body);
}
if (isClientError(response) || isServerError(response)) {
LOGGER.error("Error[Client]|statusCode|{}, body|{}", response.getStatusCode(), CommonUtills.streamToString(response.getBody()));
throw new AccessException(response.getStatusText(),
ServiceMessage.error().code(90).payload(response.getRawStatusCode() + ":" + response.getStatusText()).build());
}
return response;
} catch (IOException exception) {
LOGGER.error("AccessError", exception);
throw new AccessException("Internal service call error",
ServiceMessage.error().code(90).payload("Internal service call error", exception.getMessage()).build()
);
} finally {
LOGGER.info("ReqCompletedOn|{}", request.getURI());
}
}
private String generateNewAccessCode() {
Optional<String> accessToken = resourceIntegration.getAccessToken();
setAccessToken(accessToken.get());
return getAccessToken();
}
private static void setAccessToken(String token) {
accessToken = token;
}
private static String getAccessToken() {
return accessToken;
}
private boolean isClientError(ClientHttpResponse response) throws IOException {
return (response.getRawStatusCode() / 100 == 4);
}
private boolean isServerError(ClientHttpResponse response) throws IOException {
return (response.getRawStatusCode() / 100 == 5);
}
private boolean unauthorized(ClientHttpResponse response) throws IOException {
return (response.getStatusCode().value() == HttpStatus.UNAUTHORIZED.value());
}
private boolean isBasicUri(HttpRequest request) {
return Objects.equals(request.getURI().getRawPath(), "/apicall/token");
}
private boolean isMifeRequest(HttpRequest request) {
return request.getURI().toString().startsWith("https://api.examplexx.com/");
}
}
Token generate method- In resourceIntegration
public Optional<String> getAccessToken() {
ResponseEntity<AccessTokenResponse> res = getRestTemplate().exchange(
getAccessTokenGenUrl(),
HttpMethod.POST,
null,
AccessTokenResponse.class
);
if (res.hasBody()) {
LOGGER.info(res.getBody().toString());
return Optional.of(res.getBody().getAccess_token());
} else {
return Optional.empty();
}
}
Another sample protected API call method
public Optional<String> getMobileNumberState(String msisdn) {
try {
String jsonString = getRestTemplate().getForObject(
getQueryMobileSimImeiDetailsUrl(),
String.class,
msisdn
);
ObjectNode node = new ObjectMapper().readValue(jsonString, ObjectNode.class);
if (node.has("PRE_POST")) {
return Optional.of(node.get("PRE_POST").asText());
}
LOGGER.debug(jsonString);
} catch (IOException ex) {
java.util.logging.Logger.getLogger(RestApiConsumerService.class.getName()).log(Level.SEVERE, null, ex);
}
return Optional.empty();
}
Problem
Here's the log of mock API,
//first time no Bearer token, this returns 401 for API /simulate/unauthorized
accept:text/plain, application/json, application/*+json, */*
authorization:Bearer null
/simulate/unauthorized
//then it sends Basic request to get a token, this is the log
accept:application/json, application/*+json
authorization:Basic M3ZLYmZQbE1ERGhJZWRHVFNiTEd2Vlh3RThnYTp4NjJIa0QzakZUcmFkRkVOSEhpWHNkTFhsZllh
Generated Token:: 57f21374-1188-4c59-b5a7-370eac0a0aed
/apicall/token
//finally consumer API sends the previous request to access protected API and it contains newly generated token in bearer header
accept:text/plain, application/json, application/*+json, */*
authorization:Bearer 57f21374-1188-4c59-b5a7-370eac0a0aed
/simulate/unauthorized
The problem is even-though mock API log had the correct flow, consumer API does not get any response for third call, here's the log of it (unnecessary logs are omitted).
RequestInterceptor.intercept() - ReqOn|URI:[GET]http://localhost:8080/simulate/unauthorized?x=GlobGlob, Headers|{Accept=[text/plain, application/json, application/*+json, */*], Content-Length=[0]}, Body|
RequestInterceptor.intercept() - ClientResponse:[BEARER], status|401 UNAUTHORIZED
RequestInterceptor.intercept() - GetToken Res|401 UNAUTHORIZED
RequestInterceptor.intercept() - ReqOn|URI:[POST]http://localhost:8080/apicall/token?grant_type=client_credentials, Headers|{Accept=[application/json, application/*+json], Content-Length=[0]}, Body|
RequestInterceptor.intercept() - ClientResponse:[BASIC], status|200 OK
RequestInterceptor.intercept() - ReqCompletedOn|http://localhost:8080/apicall/token?grant_type=client_credentials
RestApiConsumerService.getAccessToken() - |access_token2163b0d4-8d00-4eba-92d0-7e0bb609b982,scopeam_application_scope default,token_typeBearer,expires_in34234|
RequestInterceptor.intercept() - NewToken|2163b0d4-8d00-4eba-92d0-7e0bb609b982
RequestInterceptor.intercept() - ReqCompletedOn|http://localhost:8080/simulate/unauthorized?x=GlobGlob
http://localhost:8080/simulate/unauthorized third time does not return any response, but mock API log says it hit the request. What did i do wrong ?, is it possible to achieve this task using this techniques ? or is there any other alternative way to do this ? any help is highly appreciated.
I have tried this:
Add an interceptor ClientHttpRequestInterceptor
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StringUtils;
public class RequestResponseHandlerInterceptor implements ClientHttpRequestInterceptor {
#Autowired
private TokenService tokenService;
#Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String AUTHORIZATION = "Authorization";
/**
* This method will intercept every request and response and based on response status code if its 401 then will retry
* once
*/
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
ClientHttpResponse response = execution.execute(request, body);
if (HttpStatus.UNAUTHORIZED == response.getStatusCode()) {
String accessToken = tokenService.getAccessToken();
if (!StringUtils.isEmpty(accessToken)) {
request.getHeaders().remove(AUTHORIZATION);
request.getHeaders().add(AUTHORIZATION, accessToken);
//retry
response = execution.execute(request, body);
}
}
return response;
}
}
Apart from this you need to override RestTemplate initialization as well.
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(new RequestResponseHandlerInterceptor()));
return restTemplate;
}
i´m implementing a Restful service using Jax-RS 2.0 (Resteasy 3.0.7.Final) and share the interface between client and service.
The return value is void because ClientResponse is deprecated since RestEasy introduced JAX-RS 2.0 in version 3+.
To return the location of the new created object i inject the response, using the #Context annotation, and add the Content-Location header.
For example:
Shared Interface:
#Path("/")
#Consumes("application/xml")
#Produces("application/xml")
interface Resource {
#Path("createSomething")
void createSomething(AnyObject object);
...
}
Implementation class (The Service):
class ResourceImpl {
...
#Context org.jboss.resteasy.spi.HttpResponse response;
...
#Override
void createSomething(AnyObject object) throws AnyException {
String id = service.create(object);
response.getOutputHeaders().putSingle("Content-Location",
"/createSomething/" + id);
response.setStatus(Response.Status.CREATED.getStatusCode());
}
}
The client (build with the Resteasy Proxy Framework):
...
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target(baseUrl);
Resource resource = (Resource) target.proxy(Resource.class);
resource.createSomething(anyObject);
...
How can i retrieve Header information (and others, like Atom Links) which has been injected by the service?
Is it reasonable to use client side Filters and Interceptors?
Thank You
The best solution i found was to use a Filter to process the incoming response header.
public class HeaderFilter implements ClientResponseFilter {
private Map<String, String> headers = new HashMap<>();
private List<String> headerFilter = new ArrayList<>();
public final void addHeaderFilter(final String header) {
headerFilter.add(header);
}
public final void removeHeaderFilter(final String header) {
headerFilter.remove(header);
}
public final String getHeader(final String header) {
return headers.get(header);
}
#Override
public final void filter(final ClientRequestContext requestContext,
final ClientResponseContext responseContext)
throws IOException {
headers = new HashMap<>();
for (String headerToLookFor : headerFilter) {
String header = responseContext.getHeaderString(headerToLookFor);
if (header != null) {
headers.put(headerToLookFor, header);
} else {
...
}
}
}
}
I am trying to set/request gZip in HTTP REQUEST HEADERS inside my Java CXF WS Client BUT for some reason its being IGNORED. I don't get back gZipped response. Here is how I am trying to set. I am using Apache CXF 2.3.2. What am I missing?
public class LoggerXML implements SOAPHandler<SOAPMessageContext> {
private String uniqueIdentifier;
private String sessionId;
public LoggerXML(String sessionId, String uniqueIdentifier) {
this.sessionId = sessionId;
this.uniqueIdentifier = uniqueIdentifier;
}
protected final void setLogStream(PrintStream ps) {
// out = ps;
}
public void init(Map c) {
uniqueIdentifier = "";
}
public Set<QName> getHeaders() {
return null;
}
public boolean handleMessage(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean)
smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if(outboundProperty){
// Creating HTTP headers & setting gZip.
Map<String, List<String>> headers = (Map<String,
List<String>>) smc.get(MessageContext.HTTP_REQUEST_HEADERS);
if(headers == null){
//System.out.println("LoggerXML.handleMessage: headers = null");
headers = new HashMap<String, List<String>>();
}
// Add HTTP headers to the web service request
headers.put("Accept-Encoding", Collections.singletonList("gzip,deflate"));
//headers.put("Content-Encoding", Collections.singletonList("gzip"));
//headers.put("Accept-Encoding", Collections.singletonList("gzip"));
smc.put(MessageContext.HTTP_REQUEST_HEADERS, headers);
//smc.put("org.apache.cxf.transport.common.gzip.GZIPOutInterceptor.UseGzip","YES");
}
return true;
}
public boolean handleFault(SOAPMessageContext smc) {
return true;
}
// nothing to clean up
public void close(MessageContext messageContext) {
}
// nothing to clean up
public void destroy() {
}
// Other Methods....
}
This code works for me
// Get the underlying Client object from the proxy object of service interface
Client proxy = ClientProxy.getClient(stub);
// Creating HTTP headers
Map<String, List<String>> headers = new HashMap<String, List<String>>();
headers.put("Accept-Encoding", Arrays.asList("gzip"));
// Add HTTP headers to the web service request
proxy.getRequestContext().put(Message.PROTOCOL_HEADERS, headers);
Refer:http://singztechmusings.wordpress.com/2011/09/17/apache-cxf-how-to-add-custom-http-headers-to-a-web-service-request/
I would like to configure my JAX-WS client to send messages in ISO-8859-1. Currently UTF-8 is used.
Here is what the client tries to do:
Map<String, Object> reqContext = ((BindingProvider) service).getRequestContext();
Map httpHeaders = new HashMap();
httpHeaders.put("Content-type",Collections.singletonList("text/xml;charset=ISO-8859-1"));
reqContext.put(MessageContext.HTTP_REQUEST_HEADERS, httpHeaders);
But this setting is ignored and tcpmon shows that the following is received by the server:
POST /service/helloWorld?WSDL HTTP/1.1
Content-type: text/xml;charset="utf-8"
Soapaction: "helloWorld"
Accept: text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
User-Agent: Oracle JAX-WS 2.1.5
Host: 1.1.1.1:8001
Connection: keep-alive
Content-Length: 4135
<?xml version='1.0' encoding='UTF-8'?>
<S:Envelopexmlns:S="http://schemas.xmlsoap.org/soap/envelope/">...
So, the setting is overriden and UTF-8 is used, both in the HTTP header and in the XML message. The service is defined by the WSDL which is encoded in UTF-8.
Q: Should I redefine the service WSDL to be encoded in ISO-8899-1 and then regenerate the client? Or, is it that I am just not setting the HTTP headers properly?
Using handler:
public class MyMessageHandler implements SOAPHandler<SOAPMessageContext> {
#Override
public boolean handleMessage(SOAPMessageContext context) {
Boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound.booleanValue()) {
try {
context.getMessage().setProperty(SOAPMessage.CHARACTER_SET_ENCODING,
"ISO-8859-1");
}
catch (SOAPException e) {
throw new RuntimeException(e);
}
}
return true;
}
And register the handler:
BindingProvider bindProv = (BindingProvider) service;
List<Handler> handlerChain = bindProv.getBinding().getHandlerChain();
handlerChain.add(new MyMessageHandler ());
The answer from jaypi seems right. But I needed to add some default implementations. Also it was easy to put inline:
UPDATE: I guess you have to set the handlerChain explicitly. changing the result of getHandlerChain will do nothing.
List<Handler> chain = bindingProvider.getBinding().getHandlerChain();
chain.add(new SOAPHandler<SOAPMessageContext>() {
#Override
public boolean handleMessage(SOAPMessageContext context) {
LOG.info("BaseService.handleMessage" + context);
Boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound.booleanValue()) {
try {
context.getMessage().setProperty(SOAPMessage.CHARACTER_SET_ENCODING, "ISO-8859-1");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return true;
}
#Override
public boolean handleFault(SOAPMessageContext context) {
return true;
}
#Override
public void close(MessageContext context) {
}
#Override
public Set<QName> getHeaders() {
return null;
}
});
bindingProvider.getBinding().setHandlerChain(chain);
Here is the request
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soap="http://soap.ws.server.wst.fit.cvut.cz/">
<soapenv:Header>
<userId>someId</userId>
</soapenv:Header>
<soapenv:Body>
...
</soapenv:Body>
</soapenv:Envelope>
and I want to get that userId.
I tried this
private List<Header> getHeaders() {
MessageContext messageContext = context.getMessageContext();
if (messageContext == null || !(messageContext instanceof WrappedMessageContext)) {
return null;
}
Message message = ((WrappedMessageContext) messageContext).getWrappedMessage();
return CastUtils.cast((List<?>) message.get(Header.HEADER_LIST));
}
private String getHeader(String name) {
List<Header> headers = getHeaders();
if (headers != null) {
for (Header header : headers) {
logger.debug(header.getObject());
// return header by the given name
}
}
return null;
}
And it logs [userId : null]. How can I get the value and why is null there?
"[userId : null]" is generally the "toString" printout of a DOM element. Most likely if you do something like
logger.debug(header.getObject().getClass())
you will see that it is a DOM Element subclass of somesort. Thus, something like:
logger.debug(((Element)header.getObject()).getTextContent())
might print the text node.
import javax.xml.soap.*;
SOAPPart part = request.getSOAPPart();
SOAPEnvelope env = part.getEnvelope();
SOAPHeader header = env.getHeader();
if (header == null) {
// Throw an exception
}
NodeList userIdNode = header.getElementsByTagNameNS("*", "userId");
String userId = userIdNode.item(0).getChildNodes().item(0).getNodeValue();
You can get soap headers without Interceptors and without JAXB.
In your service_impl class add :
public class YourFunctionNameImpl implements YourFunctionName{
#Resource
private WebServiceContext context;
private List<Header> getHeaders() {
MessageContext messageContext = context.getMessageContext();
if (messageContext == null || !(messageContext instanceof WrappedMessageContext)) {
return null;
}
Message message = ((WrappedMessageContext) messageContext).getWrappedMessage();
List<Header> headers = CastUtils.cast((List<?>) message.get(Header.HEADER_LIST));
return headers;
}
...
Then in your function you can use:
List<Header> headers = getHeaders();
for(Iterator<Header> i = headers.iterator(); i.hasNext();) {
Header h = i.next();
Element n = (Element)h.getObject();
System.out.println("header name="+n.getLocalName());
System.out.println("header content="+n.getTextContent());
}
We can get SOAP header in server side by adding following code in CXF interceptor.
Create a class like
public class ServerCustomHeaderInterceptor extends AbstractSoapInterceptor {
#Resource
private WebServiceContext context;
public ServerCustomHeaderInterceptor() {
super(Phase.INVOKE);
}
#Override
public void handleMessage(SoapMessage message) throws Fault,JAXBException {
System.out.println("ServerCustomHeaderInterceptor handleMessage");
JAXBContext jc=null;
Unmarshaller unmarshaller=null;
try {
jc = JAXBContext.newInstance("org.example.hello_ws");
unmarshaller = jc.createUnmarshaller();
} catch (JAXBException e) {
e.printStackTrace();
}
List<Header> list = message.getHeaders();
for (Header header : list) {
ElementNSImpl el = (ElementNSImpl) header.getObject();
ParentNode pn= (ParentNode) el.getFirstChild();
//Node n1= (Node) pn;
//Node n1= (Node) el.getFirstChild();
CustomHeader customHeader=(CustomHeader) unmarshaller.unmarshal(el.getFirstChild());
}
}
After this we need to inject this as a interceptor like
<jaxws:inInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
<bean class="org.example.hellows.soap12.ServerCustomHeaderInterceptor" />
</jaxws:inInterceptors>
in your server context xml.
We may need to change few lines as per your requirements. Basic flow will work like this.
Having a MessageContext messageContext, you can use this code:
HeaderList hl = (HeaderList) messageContext.get(JAXWSProperties.INBOUND_HEADER_LIST_PROPERTY);
which gives you access to all SOAP headers.