Getting response from java soap handler - java

I will be extremely grateful if someone share his experience in solving the following problem.
I have a SOAP service in the JDK implementation (which is Metro, I believe).
For the logging purpose we need to extract the body of both the incoming request and generated response.
I try to fetch it by implementing a SOAPHandler on the server side.
I configure handler as a Spring bean.
All the examples I found essentially replicate the example from the Oracle documentation: https://docs.oracle.com/cd/E23943_01/web.1111/e13734/handlers.htm#WSADV170:
public boolean handleMessage(SOAPMessageContext messageContext)
{
Boolean outboundProperty = (Boolean)
messageContext.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
System.out.println("\nOutbound message:");
} else {
System.out.println("\nInbound message:");
}
System.out.println("** Response: "+messageContext.getMessage().toString());
return true;
}
Here one reads one of the Boolean properties of the SOAP message context which, as I see it, corresponds to either request or response.
But debugger in my experiments never enters the branch corresponding to response (else-branch). How is such handler supposed to trace both request and response?
I also wonder what message is read as messageContext.getMessage(): is it incoming (request) or outbound (response)
I wonder now is it possible indeed by implementing handleMessage() method to get access to both request and response?
Does a single handler intercepts both request and its response?
Did I misunderstand the example?
And ... SOAPHandler - is it a specific instance for every request (request-response pair)?
Thank you

Try this for SoapHandler:
Boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (isRequest) {
//handle request
} else {
//handle response
}
And this for LogicalHandler:
Boolean outboundProperty = (Boolean)
messageContext.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
System.out.println("\nOutbound message:");
} else {
System.out.println("\nInbound message:");
}

Related

SoapHandler triggered prematurely

I intercept the incoming and outgoing soap messages using a SoapHandler like this:
#Override
public boolean handleMessage(SOAPMessageContext context) {
boolean isResponse = (Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if(!isResponse){
logger.debug("MySoapHandler.handleMessage(): this is a soap request");
SOAPMessage soapMsg = context.getMessage();
SOAPMessage = createNewSoapRequest(soapMsg);
context.setMessage(newSoapMsg);
}
else {
logger.debug("MySoapHandler.handleMessage(): this is a soap response");
SOAPMessage soapMsg = context.getMessage();
SOAPMessage newSoapMsg = createNewSoapResponse(soapMsg);
context.setMessage(newSoapMsg);
}
return true;
}
I receive the incoming soap message and replace it with a new one and send it on its way. I can monitor the incoming message and its replacement and everything looks right. If I comment out the incoming part and test the outgoing part by sending the correct request using SoapUI, the outgoing soap response is intercepted and replaced with a new message. That too seems to work ok.
When I un-comment the incoming portion and let er' rip, the incoming one is triggered correctly, but the outgoing response seems to get triggered prematurely, before the main code for generating it has completed. Can anyone shed some light?

SOAP-WS security header authentication

I have developed a webservices using spring+ XSD+ Payload. I have a requirement of authenticating the request header with username and password coming in SOAP request header which i achieved with SOAPUI
I m able to generate the below header in the request
<soapenv:Envelope xmlns:jaxb="http://jaxb.miws.sg.com/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis- 200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-C3092BFBAE5B212E93144378035575013">
<wsse:Username>User</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">test</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">CT1Fyo/g2WMaadE52bsnkg== </wsse:Nonce>
<wsu:Created>2015-10-02T10:05:55.750Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
Now i want to validate the header elements for userName and Password.
Ex:
Case 1:
userName=User and Password=test //Authentication passed and give response Success
Case 2:
userName=User1 and Password=test1 //Authentication failed and give response Failure
Please help me to provide the suitable samples to achieve same.
Handlers in SOAP webservices (similar to Interceptors/Filters) can be used for the authentication purpose on the server side and then chaining the request further.
Please have a look at SOAPHandler to parse the header information from the payload and authenticating the username/password.
SOAP Handler at Server Side
Here are some steps to do that:
Implement a SOAPHandler class by writing a custom handleMessage method.
Within the handleMessage method, evaluate the context's MESSAGE_OUTBOUND_PROPERTY. If it is false (meaning it is an inbound message), then write code that introspects the context.getMessage(). There you can evaluate the MIME headers, the security headers & tokens and the body, to determine if you need to reject the authentication credential. If you do, return false at the end of the method.
Add the SoapHander you created to the service's Handler chain.
Example of a SOAPHandler:
public class MyCustomSoapHandler implements SOAPHandler<SOAPMessageContext>
{
public Set<QName> getHeaders()
{
return Collections.emptySet();
}
public boolean handleMessage(SOAPMessageContext messageContext)
{
Boolean outboundProperty = (Boolean)
messageContext.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
//This is for handling messages going out of the conduit
} else {
//Here is where you want to authenticate
}
return true; //return false if do not want to proceed to the next handler in the chain
}
public boolean handleFault(SOAPMessageContext messageContext)
{
return true;
}
public void close(MessageContext messageContext)
{
}
Here a starter template for your SOAPHandler that you need to add to your Service's handlerChain:
#WebService(name = "Handler", targetNamespace = "http://example.org")
#HandlerChain(file="handler-chain.xml")
public class HandlerWS
{
#Resource
WebServiceContext ctx;
#WebMethod()
public String getProperty(String propertyName)
{
return (String) ctx.getMessageContext().get(propertyName);
}
}
You'll also need to add the handler-chain.xml to your classpath:
examples.webservices.handler.Handler1
examples.webservices.handler.Handler2
For a complete guide, see Oracle's guide to creating SOAPHandlers

SOAP Service Client doesn't send Mime headers for Basic Auth

Some background: This is a Weblogic Web Services created Service client creates via Eclipse. I believe this uses clientgen behind the scenes.
I'm trying to make a SOAP call that requires preemptive Basic Authentication. The request is being sent but the Mimeheaders I'm setting are not going with it. The recipient of the call has informed me that the request itself is coming through but any mimeheaders I set are not.
The service call is rather simple.
DescriptionService service = new DescriptionService(wsdlLocation, new QName("urn:descriptionService.service.company.com", "DescriptionService"));
service.setHandlerResolver(new HandlerResolver() {
#SuppressWarnings("rawtypes")
#Override
public List<Handler> getHandlerChain(final PortInfo portInfo) {
final List<Handler> handlerList = new ArrayList<Handler>();
handlerList.add(new SOAPDescriptionServiceHeaderHandler());
return handlerList;
}
});
DescriptionServicePortType portType = service.getDescriptionServicePort();
DescriptionRequest request = new DescriptionRequest();
request.setId(id);
DescriptionResponse description = portType.describe(request);
The handler is where I set the Mimeheaders:
#Override
public boolean handleMessage(final SOAPMessageContext context) {
final Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
final SOAPMessage message = context.getMessage();
if (outboundProperty.booleanValue()) {
try {
MimeHeaders mimeheaders = message.getMimeHeaders();
String encodedAuth = Base64.encode(new String("un:pw").getBytes());
mimeheaders.addHeader("Authorization", "Basic " + encodedAuth);
this.logMessage(message, outboundProperty.booleanValue(), false);
} catch (final Exception e) {
// Log Error
}
} else {
this.logMessage(message, outboundProperty.booleanValue(), false);
}
return true;
}
It does hit this handler and set the mimeheaders. If I set a break point and look at the mime headers before it leaves the handleMessage method, I can see that they are set.
I'm able to call the request and get a response in SoapUI. I set up preemptive basic auth and it works fine. When I send the request through the Java Client, I get no response and actually get an error that says it's the incorrect content type. I believe this error is referring to the fault response as I don't actually get the response (doesn't hit the handleMessage() method in the handler either) and I know the request is going through with text/xml which is what the error is asking for.
I'm unsure if it has something to do with the "preemptive" requirement? Is there a way to set basic auth set up this way as preemptive?
Thoughts?
Any assistance would be appreciated.
Basic Auth is done at the HTTP layer, not the SOAP layer, so you need to configure the underlying HTTP library. (MIME headers have nothing to do with it)
For example for CXF, have a look at this question HTTP basic authentication through CXF interceptor not working

Retrofit connection failure returns RetrofitError.response as null

Using Retrofit 1.6.0, OkHTTP 2.0.0, and OkHTTP-UrlConnection 2.0.0.
I am doing a POST to a service using Retrofit to a URL that does not exist. The failure callback is called, as expected. However, the RetrofitError parameter does not have a response. I would really like to grab the HTTP status code that was returned by using
error.getResponse().getStatus()
but since getResponse() returns null, I can't.
Why is getResponse() null and how can I get the status?
Thanks.
Also, the error I am receiving is UnknownHostException, as expected. Repeat: I am expecting this error. I want to know how to get the HTTP status code or why error.getResponse() returns null.
Edit: Here's some code:
RestAdapterBuilderClass.java
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("http://badURL.DoesntMatter/");
.setRequestInterceptor(sRequestInterceptor)
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
sService = restAdapter.create(ServiceInterface.class);
ServiceInterface.java
#POST("/Login")
void login(#Body JsonObject body, Callback<String> callback);
CallbackClass.java
#Override
public void failure(RetrofitError error) {
if (error.getResponse() == null) {
// error.getResponse() is null when I need to get the status code
// from it.
return;
}
}
When you get an UnknownHostException it means, that you were not able to establish a connection to the server. You cannot, in fact, expect a HTTP status in that case.
Naturally you only get a Http response (and with that a status) when you can connect to a server.
Even when you get a 404 status code, you made a connection to the server. That is not the same as a UnknownHostException.
The getResponse() can return null if you didn't get a response.
RetrofitError has a method called isNetworkError() that you can use to detect a failed request due to network problems. I usually add a small helper method like this:
public int getStatusCode(RetrofitError error) {
if (error.isNetworkError()) {
return 503; // Use another code if you'd prefer
}
return error.getResponse().getStatus();
}
and then use that result to handle any additional failure logic (logout on 401, display error message on 500, etc).
Just to expand on #lyio's answer, I found from Fabric logging that getKind() sometimes returns UNEXPECTED and then if you parse the message you get timeouts and connection issues so I wrote the utility class below.
public class NetworkUtils {
// Compiled from Fabric events
private static final List<String> networkErrorStrings = new ArrayList<>(Arrays.asList(
"Unable to resolve host",
"Connection closed by peer",
"Failed to connect",
"timeout",
"Connection timed out"));
public static boolean isNetworkError(#Nullable RetrofitError retrofitError) {
if (retrofitError != null) {
if (retrofitError.getKind() != RetrofitError.Kind.NETWORK) {
for (String error : networkErrorStrings) {
if (retrofitError.getMessage().contains(error)) {
return true;
}
}
} else {
return true;
}
}
return false;
}
}
I am using Retrofit 2. When endpoint url end with "/" as in your case and again in your interface it starts with "/" [#POST("/Login")] causes this problem. Remove the "/" from .setEndpoint() method

Can I wrap all JAX-RS requests with custom pre-dispatch, post-dispatch and error-handler code?

I have a number of classes exposed as JAX-RS request "handlers", using javax.ws.rs.Path annotations. I want to add certain actions before every request and after each request. Also, I need to create a global application-wide exception handler, which will catch everything thrown by these handlers and protocol.
Is it possible to achieve this with standard JAX-RS without creating of a custom class inherited from com.sun.jersey.spi.container.servlet.ServletContainer (I'm using Jersey).
You can also use ExceptionMappers. This mechanism which catch the exception thrown by your service and convert it to the appropriate Response:
#Provider
public class PersistenceMapper implements ExceptionMapper<PersistenceException> {
#Override
public Response toResponse(PersistenceException arg0) {
if(arg0.getCause() instanceof InvalidDataException) {
return Response.status(Response.Status.BAD_REQUEST).build();
} else {
...
}
}
}
For more information see:
JAX-RS using exception mappers
You could create a proxy RESTful service and use this as the entry point to all your other RESTful services. This proxy can receive requests, do any pre-processing, call the RESTful service required, process the response and then return something to the caller.
I have a set up like this in a project I've been working on. The proxy performs functions like authentication, authorisation and audit logging. I can go into further details if you like.
Edit:
Here is an idea of how you might want to implement a proxy that supports GET requests;
#Path("/proxy")
public class Proxy
{
private Logger log = Logger.getLogger(Proxy.class);
#Context private UriInfo uriInfo;
#GET
#Path("/{webService}/{method}")
public Response doProxy(#Context HttpServletRequest req,
#PathParam("webService") String webService,
#PathParam("method") String method)
{
log.debug("log request details");
//implement this method to work out the URL of your end service
String url = constructURL(req, uriInfo, webService, method);
//Do any actions here before calling the end service
Client client = Client.create();
WebResource resource = client.resource(url);
try
{
ClientResponse response = resource.get(ClientResponse.class);
int status = response.getStatus();
String responseData = response.getEntity(String.class);
log.debug("log response details");
//Do any actions here after getting the response from the end service,
//but before you send the response back to the caller.
return Response.status(status).entity(responseData).build();
}
catch (Throwable t)
{
//Global exception handler here
//remember to return a Response of some kind.
}
}
You can use filters to read and modify all requests and responses.

Categories