I generated my client soap from wsimport JAX-WS, I have already consumed others webservice that it had fault message mapped, but the service current doesn't have.
When I call the service and it returns fault message I can't get the message in the Java, but if call from soapUI I can see the error.
The fault message is the same of the success, generated from JAX-WS.
My code:
//before I setter my request
try{
IPGApiOrderService iPGApiOrderService = new IPGApiOrderService();
IPGApiOrder client = iPGApiOrderService.getIPGApiOrderSoap11();
IPGApiOrderResponse response = client.ipgApiOrder(request)
}catch (SOAPFaultException soapEx) {
System.out.println("Fault ............. " + soapEx.getFault());
System.out.println("Detail ............ " + soapEx.getFault().getDetail());
System.out.println("FaultCode.......... " + soapEx.getFault().getFaultCode());
System.out.println("FaultActor......... " + soapEx.getFault().getFaultActor());
System.out.println("Message............ " + soapEx.getMessage());
soapEx.printStackTrace();
}
follow the out
Fault ............. [SOAP-ENV:Fault: null]
Detail ............ [detail: null]
FaultCode.......... SOAP-ENV:Client
FaultActor......... null
Message............ Client received SOAP Fault from server: ProcessingException Please see the server log to find more detail regarding exact cause of the failure.
com.sun.xml.internal.ws.fault.ServerSOAPFaultException: Client received SOAP Fault from server: ProcessingException Please see the server log to find more detail regarding exact cause of the failure.
at com.sun.xml.internal.ws.fault.SOAP11Fault.getProtocolException(SOAP11Fault.java:178)
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.createException(SOAPFaultBuilder.java:124)
at com.sun.xml.internal.ws.client.sei.StubHandler.readResponse(StubHandler.java:238)
at com.sun.xml.internal.ws.db.DatabindingImpl.deserializeResponse(DatabindingImpl.java:189)
at com.sun.xml.internal.ws.db.DatabindingImpl.deserializeResponse(DatabindingImpl.java:276)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:104)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:77)
at com.sun.xml.internal.ws.client.sei.SEIStub.invoke(SEIStub.java:147)
at com.sun.proxy.$Proxy36.ipgApiOrder(Unknown Source)
at com.firstdata.test.demo.MainTest.main(MainTest.java:53)
I resolved my problem with following steps.
Create SOAPHandler;
It'll be necessary implement 4 methods;
On method handleFault get SOAPMessageContext -> SOAPMessage -> SOAPBody -> Fault -> Detail -> add detail with xml error or some information do you want.
3.1 Fault you can put fault code, if API you was consuming always return one code error to API fault.
4. On exception you find that detail you set and work with it.
Code:
public class SOAPHandlerImpl implements SOAPHandler<SOAPMessageContext> {
public static final QName JSON_ERROR = new QName("json-error");
public boolean handleMessage(SOAPMessageContext smc) {
SOAPMessage message = smc.getMessage();
Boolean isOut = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
return isOut;
}
#Override
public boolean handleFault(SOAPMessageContext context) {
SOAPMessage message = context.getMessage();
try {
StringOutputStream str = new StringOutputStream();
message.writeTo(str);
ErrorDTO dto = XmlUtil.buildErroDTO(str.toString());
Detail detail = message.getSOAPBody().getFault().getDetail();
Gson gson = new Gson();
String obj = gson.toJson(dto);
detail.addDetailEntry(JSON_ERROR).addTextNode(obj);
message.getSOAPBody().getFault().setFaultCode(String.valueOf(dto.getTransactionId()));
} catch (Exception e) {
System.out.println("Exception in handler: " + e);
}
return true;
}
#Override
public void close(MessageContext context) {
// TODO Auto-generated method stub
}
#Override
public Set<QName> getHeaders() {
// TODO Auto-generated method stub
return null;
}
}
Catch exception
} catch (SOAPFaultException sopex) {
ErrorDTO error = null;
Iterator childElements = sopex.getFault().getDetail().getChildElements();
while (childElements.hasNext()) {
DetailEntry next = (DetailEntry) childElements.next();
if (SOAPHandlerImpl.JSON_ERROR.getLocalPart().equals(next.getNodeName())) {
Gson gson = new Gson();
error = gson.fromJson(next.getValue(), ErrorDTO.class);
}
}
String message = null;
if(error.getProcessorResponseCode() != null) {
message = ErrorApiUtil.getInstance().getMessage(error.getProcessorResponseCode());
}else {
message = error.getMessage();
}
throw new BusinessException(message);
}
Related
I try to receive a protobuf message out of RabbitMQ using Spring Integration.
My integration flow:
public class FacadeIntegrationFlowAdapter extends IntegrationFlowAdapter {
#SuppressWarnings("rawtypes")
private final Facade facade;
private final FacadeProperties facadeProperties;
#SuppressWarnings("unchecked")
#Override
protected IntegrationFlowDefinition<?> buildFlow() {
return from(facadeProperties.getQueueName())
.handle(facade::getNewMessages);
}
}
The getNewMessages method:
#Override
public ExchangeResponse getNewMessages(Message<ExchangeRequest> message) {
ExchangeRequest request = message.getPayload();
log.info("Receiving new message: " + request.toString());
This is how I send the message to the queue. It's so simple to make the test easy to follow.
ExchangeRequest request = ExchangeRequest.newBuilder()
.addAllAuthors(List.of("author1", "author2"))
.addAllBooks(List.of("book1", "book2"))
.build();
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setUsername("user");
connectionFactory.setPassword("password");
connectionFactory.setHost("localhost");
connectionFactory.setPort(24130);
try {
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
var basicProperties = new AMQP.BasicProperties().builder()
.contentType("application/x-protobuf")
.type(request.getDescriptorForType().getFullName())
.build();
channel.basicPublish(
"facade-exchange", "facade-routing-key", basicProperties, request.toByteArray());
} catch (IOException e) {
Unfortunately, I keep getting the exception:
com.google.protobuf.InvalidProtocolBufferException: Type of the Any message does not match the given class.
However, when I change the getNewMessages method to the following, all seems fine.
#Override
public ExchangeResponse getNewMessages(Message message) {
try {
Any payload = (Any) message.getPayload();
ByteString value = payload.getValue();
ExchangeRequest request = ExchangeRequest.parseFrom(value);
log.info("Receiving new message: " + request.toString());
Where do I make a mistake? Tx!
I am implementing SOAP web services with Apache CXF. I am using Jboss EAP server. I have used following code to expose SOAP web services.
CxfComponent cxfComponent = new CxfComponent(context);
CxfEndpoint serviceEndpoint = new CxfEndpoint(FPSoapServiceConstants.WSDL_CONFIG_URI, cxfComponent);
serviceEndpoint.setDataFormat(DataFormat.PAYLOAD);
serviceEndpoint.setServiceClass(com.fp.en.webservices.fulfillment.FulfillmentService.class);
HashMap<String, Object> properties = new HashMap<>();
properties.put("faultStackTraceEnabled", true);
properties.put("exceptionMessageCauseEnabled", true);
serviceEndpoint.configureProperties(properties);
serviceEndpoint.setLoggingFeatureEnabled(true);
context.addEndpoint(FPSoapServiceConstants.SOAP_ENDPOINT_FULFILLMENT_SERVICE, serviceEndpoint);
I am using apache camel to process incoming soap message
route.process(fpSOAPRequestProcessor).process(xyzProcessor).process(fpSOAPResponseProcessor)
I want to get all parameters in an object I created a class and try to get body
BuyProductRequest buyRequest = message.getBody(BuyProductRequest.class);
but this is giving me null. But when I try to get
String buyRequest = message.getBody(String.class);
It is giving me SOAP message So I have to convert xml SOAP message to Object by JAXB Marshaller.
Processor code is as follows
public class FPSoapRequestProcessor implements Processor{
#Override
public void process(Exchange exchange) throws Exception {
Message message = exchange.getIn();
String operation = String.valueOf(exchange.getIn().getHeader("operationName"));
if(FPSoapServiceConstants.BUY_PRODUCT_SOAP_OPERATION.equalsIgnoreCase(operation)) {
populateBuyProductOperationProperties(message);
}
}
private void populateBuyProductOperationProperties(Message message) {
String buyRequest = message.getBody(String.class);
BuyProductRequest productInfo= parseRequest(buyRequest);
message.setHeader("MSISDN", productInfo.getMsisdn());
message.setHeader("iname", productInfo.getIname());
message.setHeader("input", productInfo.getInput());
message.setHeader("username", productInfo.getUserName());
message.setHeader("password", productInfo.getPassword());
message.setHeader("soapConversion", true);
}
private BuyProductRequest parseRequest(String soapRequest){
try(InputStream is = new ByteArrayInputStream(soapRequest.getBytes())) {
JAXBContext jaxbContext = JAXBContext.newInstance(BuyProductRequest.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
return (BuyProductRequest) jaxbUnmarshaller.unmarshal(is);
} catch (Exception e) {
throw new RuntimeException("SOAP Request Object Resolving Error",e);
}
}
}
So Is there any simple way to construct request object and Similarly at that time when I am done with processing, in fpSOAPResponseProcessor I have to convert my object into soap string then I am sending it.
fpSoapResponseProcessor code is as follows
public class FDPSoapResponseProcessor implements Processor{
#Override
public void process(Exchange exchange) throws Exception {
Message message = exchange.getIn();
FulfillmentResponse response = XmlUtil.unmarshall(message.getBody(String.class), FulfillmentResponse.class);
BuyProductResponse buyProductResponse = new BuyProductResponse();
buyProductResponse.setProductResponse(response);
String soapResponse = parse(buyProductResponse);
exchange.getOut().setBody(soapResponse);
}
private String parse(BuyProductResponse buyProductResponse) {
try(StringWriter writer = new StringWriter()){
JAXBContext jContext = JAXBContext.newInstance(BuyProductResponse.class);
Marshaller marshallObj = jContext.createMarshaller();
marshallObj.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshallObj.setProperty("com.sun.xml.bind.xmlDeclaration", Boolean.FALSE);
marshallObj.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
marshallObj.setProperty("com.sun.xml.bind.namespacePrefixMapper", new FulfillmentResponseMapper());
marshallObj.marshal(buyProductResponse, writer);
return writer.toString();
} catch(Exception e) {
throw new RuntimeException("SOAP Request String Parsing Error",e);
}
}
private static class FulfillmentResponseMapper extends NamespacePrefixMapper {
#Override
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
if(FPSoapServiceConstants.SOAP_NAMESPACE_URI.equalsIgnoreCase(namespaceUri)) {
return FPSoapServiceConstants.SOAP_PREFIX;
}
return suggestion;
}
#Override
public String[] getPreDeclaredNamespaceUris() {
return new String[] { FPSoapServiceConstants.SOAP_NAMESPACE_URI};
}
}
}
Please suggest a proper simple way If there is?
AFAICS, this is the simple way.
We're using spring-ws 2.2 on our application to consume web services. We've been happily doing this for quite some time and everything is working fine, except that now I need to access the SOAP header in the response and I just can't find a way to do this.
We are using a WebServiceTemplate (from springs-ws) configured with a Jaxb2Marshaller. The jaxb files are generated from the wsdl using xjc. The header element in my responses look something like this:
<soapenv:Header>
<v1:ResponseHeader status="OK">
<v1:description>test</v1:description>
</v1:ResponseHeader>
</soapenv:Header>
In my java class, the code that parses the response looks like this (I've stripped some irrelevant code):
public CalculationData getValues(Integer id) throws IntegrationException {
WebServiceMessageCallback callback = createCallback(soapAction);
GetValuesRequest request = toGetValues(id);
GetValuesResponse response = null;
try {
response = (GetValuesResponse) webServiceTemplate.marshalSendAndReceive(request, callback);
} catch (SOAPFaultException fault) {
log.error("Soap fault occurred during getValues " + id);
throw new IntegrationException(fault);
}
CalculationData data = fromGetValues(response);
return data;
}
Please help me find a solution for extracting the information from the SOAP header out of the response. I must be able to parse the status code which is sent as an attribute.
By the way. I also have a ResponseHeader.java jaxb class which has been generated from the schemas.
Update from final changes:
This is how my handleResponse method looks like after inlining a ClientInterceptor implementation:
#Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
SoapMessage message = (SoapMessage) messageContext.getResponse();
Iterator<SoapHeaderElement> responseHeaderElements =
message.getSoapHeader().examineAllHeaderElements();
SoapHeaderElement header = null;
if (responseHeaderElements.hasNext()) {
header = responseHeaderElements.next();
} else {
log.error("Error! No ResponseHeader found in response.");
return false;
}
String responseCode = header.getAttributeValue(new QName(STATUS_QNAME));
responseMsg.put(RESPONSE_MSG_KEY, responseCode);
return true;
}
I tried getting the ResponseHeader element by QName, but that did not seem to work for some reason. However, I only expect to get one element in the soap header anyhow, is this will work fine.
For this use case, the best solution is to use a custom WebServiceMessageExtractor, as described here:
http://veithen.github.io/2015/01/03/spring-ws-extracting-soap-headers-from-responses.html
Implement a ClientInterceptor, specifically see handleResponse() method.
In order to access the Soap Headers, convert to a SoapMessage
public final boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
QName v1ResponseHeaderQName = null;//todo
QName statusAttrQName = null;//todo
SoapMessage message = (SoapMessage) messageContext.getResponse();
Iterator<SoapHeaderElement> matchingHeaders = message.getSoapHeader().examineHeaderElements(v1ResponseHeaderQName);
String status = matchingHeaders.next().getAttributeValue(statusAttrQName);
}
then call webServiceTemplate.setInterceptors(..)
For some further examples of this stuff see AbstractWsSecurityInterceptor and its subclasses. Be aware however that those interceptors deal with replacing the request message, you just want to read the response message.
Problem is you are dealing with the raw soap message now so you've lost the nice spring marshalling and need to start dealing with namespaces (QNames) and w3c Dom stuff.
In order for the interceptor to pass the header back to the calling code, you could make the interceptor an anonymous inner class that is setup inside your getValues(...) method.
final Map<String,String> headers = new HashMap<>();
template.setInterceptors(new ClientInterceptor[]{new ClientInterceptor() {
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
headers.put("foo", "bar");
return false;
}
#Override
public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public void afterCompletion(MessageContext messageContext, Exception ex) throws WebServiceClientException {
}
}});
template.marshalSendAndReceive(....);
I have a JAX-WS 2.2 WebService and I must take the IP Address of each client that communicate with it. I write a SOAP protocol handler but I can't see the addresses because the handlers doesn't contain this information and using the mimeheaders I can't also see this information. The code of my handler is the follow:
public class AddressHandler implements SOAPHandler<SOAPMessageContext> {
private void takeIPAddress(SOAPMessageContext context) {
try {
SOAPMessage original = context.getMessage();
MimeHeaders mimeheaders = original.getMimeHeaders();
MimeHeader mimeheader = null;
Iterator<?> iter = mimeheaders.getAllHeaders();
for (; iter.hasNext();) {
mimeheader = (MimeHeader) iter.next();
System.out.println("name=" + mimeheader.getName() + ", value="
+ mimeheader.getValue());
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void close(MessageContext arg0) {
// TODO Auto-generated method stub
}
#Override
public boolean handleFault(SOAPMessageContext arg0) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean handleMessage(SOAPMessageContext context) {
takeIPAddress(context);
return true;
}
#Override
public Set<QName> getHeaders() {
// TODO Auto-generated method stub
return null;
}
}
Now I'm seeing that would be possible to see the addresses using the following code:
SOAPMessageContext jaxwsContext = (SOAPMessageContext)wsContext.getMessageContext();
HttpServletRequest request = HttpServletRequest)jaxwsContext.get(SOAPMessageContext.SERVLET_REQUEST);
String ipAddress = request.getRemoteAddr();
But I can't import correctly the HttpServletRequest class. Do you have any ideas?
UPDATE
Thanks to A Nyar Thar, I've seen that exists another method to take address and I've implemented this in my code, that now is:
private void takeIPAddress(SOAPMessageContext context) {
HttpExchange exchange = (HttpExchange)context.get("com.sun.xml.ws.http.exchange");
InetSocketAddress remoteAddress = exchange.getRemoteAddress();
String remoteHost = remoteAddress.getHostName();
System.out.println(remoteHost);
}
But the code execution create this error (where row 39 is where I do exchange.getRemoteAddress()):
java.lang.NullPointerException
at server.AddressHandler.takeIPAddress(AddressHandler.java:39)
at server.AddressHandler.handleMessage(AddressHandler.java:80)
at server.AddressHandler.handleMessage(AddressHandler.java:1)
at com.sun.xml.internal.ws.handler.HandlerProcessor.callHandleMessage(HandlerProcessor.java:282)
at com.sun.xml.internal.ws.handler.HandlerProcessor.callHandlersRequest(HandlerProcessor.java:125)
at com.sun.xml.internal.ws.handler.ServerSOAPHandlerTube.callHandlersOnRequest(ServerSOAPHandlerTube.java:123)
at com.sun.xml.internal.ws.handler.HandlerTube.processRequest(HandlerTube.java:105)
at com.sun.xml.internal.ws.api.pipe.Fiber.__doRun(Fiber.java:626)
at com.sun.xml.internal.ws.api.pipe.Fiber._doRun(Fiber.java:585)
at com.sun.xml.internal.ws.api.pipe.Fiber.doRun(Fiber.java:570)
at com.sun.xml.internal.ws.api.pipe.Fiber.runSync(Fiber.java:467)
at com.sun.xml.internal.ws.server.WSEndpointImpl$2.process(WSEndpointImpl.java:299)
at com.sun.xml.internal.ws.transport.http.HttpAdapter$HttpToolkit.handle(HttpAdapter.java:593)
at com.sun.xml.internal.ws.transport.http.HttpAdapter.handle(HttpAdapter.java:244)
at com.sun.xml.internal.ws.transport.http.server.WSHttpHandler.handleExchange(WSHttpHandler.java:95)
at com.sun.xml.internal.ws.transport.http.server.WSHttpHandler.handle(WSHttpHandler.java:80)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
at sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:83)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:80)
at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:677)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
at sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:649)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
I think that the real problem is that I don't know how take WebServiceContext from my class AddressHandler. Do you have ideas?
For JAX-WS based webservice, you can access remote host info from javax.xml.ws.spi.http.HttpExchange, that can be accessed based on JAX-WS version,
JAX-WS 2.1
SOAPMessageContext soapContext = (SOAPMessageContext)wsContext.getMessageContext();
HttpExchange exchange = (HttpExchange)soapContext.get(JAXWSProperties.HTTP_EXCHANGE);
JAX-WS 2.2
SOAPMessageContext soapContext = (SOAPMessageContext)wsContext.getMessageContext();
HttpExchange exchange = (HttpExchange)soapContext.get("com.sun.xml.ws.http.exchange");
Note that wsContext.getMessageContext() will return MessageContext. If you want it, don't cast to SOAPMessageContext, like that,
MessageContext msgContext = wsContext.getMessageContext();
Finally you can access remote address info,
InetSocketAddress remoteAddress = exchange.getRemoteAddress();
String remoteHost = remoteAddress.getHostName();
I am trying to use eway payment gateway. In this i am using Recurring payment. For recurring they provide WSDL file, by using Maven Generator, i was creating java classes from WSDL file. When i was trying to call services, it generate an error, because the service need authentication information in SOAP header. From this Link i found the Solution to add header in SOAP request using Jaxb Object. After that, when i call the SOAP services it generates different error, which confused me. following is my code for handle eway recurring services:
1. SOAP Handler
public class EwaySOAPHandler implements SOAPHandler<SOAPMessageContext>{
private JAXBElement<EWAYHeader> jaxbElement = null;
public EwaySOAPHandler(JAXBElement<EWAYHeader> jaxbElement) {
this.jaxbElement = jaxbElement;
}
public boolean handleMessage(SOAPMessageContext context) {
Boolean outBoundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
try{
if(outBoundProperty != null && outBoundProperty.booleanValue()){
Marshaller marshaller = JAXBContext.newInstance(EWAYHeader.class).createMarshaller();
SOAPHeader header = context.getMessage().getSOAPPart().getEnvelope().addHeader();
marshaller.marshal(jaxbElement, header);
}
return false;
}catch(Exception ex){
System.out.println("Problem In Handel Message");
ex.printStackTrace();
}
return true;
}
public boolean handleFault(SOAPMessageContext context) {
throw new UnsupportedOperationException("Not Supported Yet");
}
public void close(MessageContext context) {
}
public Set<QName> getHeaders() {
return new TreeSet<QName>();
}
}
2. SOAP Client
public class SOAPClient {
#Test
public void test() {
ManageRebill manageRebill = new ManageRebill();
ManageRebillSoap manageRebillSoap = manageRebill.getManageRebillSoap();
Binding binding = ((BindingProvider) manageRebillSoap).getBinding();
List<Handler> handlersList = new ArrayList<Handler>();
EWAYHeader header = new EWAYHeader();
header.setEWAYCustomerID("87654321");
header.setPassword("test");
header.setUsername("test#eway.com.au");
ObjectFactory factory = new ObjectFactory();
JAXBElement<EWAYHeader> jaxbElement = factory.createEWAYHeader(header);
EwaySOAPHandler ewaySOAPHandler = new EwaySOAPHandler(jaxbElement);
handlersList.add(ewaySOAPHandler);
binding.setHandlerChain(handlersList);
manageRebillSoap.createRebillCustomer("Mr", "Pritpal", "Singh",
"Mohali", "CHD", "Punjab", "netsol", "1610032", "india",
"abc#abc.com", "123456789", "123456789", "987654321", "Ref123",
"JavaEE Developer", "comments", "http://google.com");
}}
Following error is generate when i try to run SOAPClient test case:
com.sun.xml.internal.ws.streaming.XMLStreamReaderException: unexpected XML tag. expected: {http://www.eway.com.au/gateway/rebill/manageRebill}CreateRebillCustomerResponse but found: {http://www.eway.com.au/gateway/rebill/manageRebill}CreateRebillCustomer.
According to this error, in response they need CreateRebillCustomerResponse but return CreateRebillCustomer object. the problem is that, how i change the object and where these objects are define ?.