WEBSERVICES : How to store mal-formed XML in webservices? - java

I am hosting a web service. I wanted to handle the scenario, in case the client sends a mal-formed XML
Here is the handler I have created
public boolean handleMessage(SOAPMessageContext smc)
{
Boolean outboundProperty =
(Boolean)smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
SOAPMessage message = null;
try
{
// This handling iss required in case a mal-formed XML is sent.
message = smc.getMessage();
}
catch(Throwable t )
{
// I want to log the XML in the database
// But the problem is I don't know how to get the XML
// as message is null.
}
}
I get to land into the catch(Throwable t) block where I have no information about the XML that was sent.
What I can log into the error log table is just that a mal-formed XML has been sent by client.
Actual Requirement:
To log and store the malformed XML for tracking purposes.

Please use the following code for getting the exact line number and exception in soap message for logging purposes:
public boolean handleMessage(SOAPMessageContext mc) {
try {
final SOAPMessage message = mc.getMessage();
String i = convertToString(message);
System.out.println(i);
return true;
} catch (Exception e) {
System.out.println(e.getMessage());
// e.printStackTrace();
return false;
}
}
This Code will take care of the any malformed SOAP Message

Related

missing header in soap request java

I have created SOAP client in pure java. Also I have implemented SOAPHandler to trace the SOAP requests and responses. But when I see the sysouts the header part is missing in the request.
Note that I am calling an enterprise service and that requires a mandatory security header for the service to be called. I am getting proper response with my security header back in the response and also is being sysout.
What could be the issue for blank header trace from SOAPHandler ? Below is my handler code:
public class SOAPLoggingHandler implements SOAPHandler<SOAPMessageContext> {
private static final Logger log = LoggerFactory.getLogger(SOAPLoggingHandler.class);
public boolean handleMessage(SOAPMessageContext context) {
log.info("handleMessage");
logToSystemOut(context);
return true;
}
public boolean handleFault(SOAPMessageContext context) {
log.info("SOAP Fault Occur");
logToSystemOut(context);
return true;
}
public void close(MessageContext context) {
log.info("close");
}
public Set<QName> getHeaders() {
log.info("getHeaders");
return null;
}
protected void logToSystemOut(SOAPMessageContext smc) {
final Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
final String message = !outboundProperty.booleanValue() ? "Web Service Response\n" : "Web Service Request\n";
logMessage(smc, message);
}
protected void logToSystemOut(SOAPMessageContext smc, String message) {
logMessage(smc, message);
}
private void logMessage(SOAPMessageContext smc, String message) {
try {
final ByteArrayOutputStream oStream = new ByteArrayOutputStream();
final Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
transformer.transform(new DOMSource(smc.getMessage().getSOAPPart()), new StreamResult(oStream));
oStream.close();
log.info(message + new String(oStream.toByteArray()));
} catch (final Exception e) {
log.warn("Failed to log web service message", e);
}
//some custom logging going on below - could be ignored
SomeLogger someLoggerObj = SomeLogger.getInstance();
try {
someLoggerObj.logSomeMessage(smc, message);
} catch (SOAPException e) {
log.error("SOAP Exception occurred :", e);
} catch (TransformerException e) {
log.error("TransformerException Exception occurred :", e);
}
}
}
My Header is seen as:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
I've experienced this week exactly the same issue, I have a chain of 5 SOAP calls with also TLS and SAML, and a mix of services implemented with AXIS and CXF, running aunder the same Tomcat 9 on OpenJDK 11. The issue happened only on production server (RedHat server) and I was not able to reproduce it on my development machine, the header was completely dropped by the java client and it was not possible to debug on the server.
I ended up installing on the target server a dedicated Tomcat 8.5 on JDK 1.8 to run CXF service separated from AXIS ones.
Hope this helps if someone has such a complex configuration, it's not a solution but a workaround.

remove security tag from MessageContext request

Sorry for multiple edits.Should have reviewed before posting
I am using the getRequest method provided by MessageContext (org.springframework.ws.context.MessageContext) to retrieve the backend soap request in an interceptor that extends ClientInterceptor. This is beind done within handleRequest method. I am fetching this to log the request into a file. When I do this , the security section within the header which has the user id and password is also getting logged. I would like to remove this before logging. Are there any available mechanisms to remove this element or should I manipulate the String in order to take the element out?
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
getPayloadFromSoapMessage((SoapMessage) messageContext.getRequest());
}
protected String getPayloadFromSoapMessage(SoapMessage message) {
String payload = "Error parsing";
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
message.writeTo(bos);
payload = bos.toString();
} catch (IOException e) {
LOG.error("Error parsing the SoapMessage", e);
}
return payload;
}

Java web service : User defined meta-data

I have a SOAP web service implementation on Jboss 4.2.3. I want to add a version number check for the service. Whenever a client makes a call, I will pass the client version number. I will write an interceptor at the server that would check the client version number. If it is a client with a different version number, I would not process the request.
What I want to know is if there is a way to pass the version number from the client in some context parameter other than adding it in the web service method signature?
In general, if I want to pass some custom META-DATA from client to server, how do I do it ?
In general, if I want to pass some custom META-DATA from client to
server, how do I do it ?
This can be achieved through SOAP Message Handlers both side (Client and Server ) in Jax-WS .
Client Side:
The custom-meta-data , like version number, UUID , Signature information can be added via SOAP Headers.
1..Write a VersionNumberHandler as shown below.
public class VersionNumberHandler implements SOAPHandler<SOAPMessageContext> {
private static final String LoggerName = "ClientSideLogger";
private Logger logger;
private final boolean log_p = true; // set to false to turn off
public VersionNumberHandler() {
logger = Logger.getLogger(LoggerName);
}
public boolean handleMessage(SOAPMessageContext ctx) {
if (log_p)
logger.info("handleMessage");
// Is this an outbound message, i.e., a request?
Boolean request_p = (Boolean) ctx
.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
// Manipulate the SOAP only if it's a request
if (request_p) {
// Get the Version Number from some property file ,
// to place in the message header.
String versionNumber = "v1.0";
try {
SOAPMessage msg = ctx.getMessage();
SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
SOAPHeader hdr = env.getHeader();
// Ensure that the SOAP message has a header.
if (hdr == null)
hdr = env.addHeader();
QName qname = new QName("http://ticket.example.com/",
"versionnumber");
SOAPHeaderElement helem = hdr.addHeaderElement(qname);
// In SOAP 1.2, setting the actor is equivalent to
// setting the role.
helem.setActor(SOAPConstants.URI_SOAP_ACTOR_NEXT);
helem.setMustUnderstand(true);
helem.addTextNode(versionNumber);
msg.saveChanges();
// For tracking, write to standard output.
msg.writeTo(System.out);
} catch (SOAPException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
}
return true; // continue down the chain
}
public boolean handleFault(SOAPMessageContext ctx) {
if (log_p)
logger.info("handleFault");
try {
ctx.getMessage().writeTo(System.out);
} catch (SOAPException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
return true;
}
public Set<QName> getHeaders() {
if (log_p)
logger.info("getHeaders");
return null;
}
public void close(MessageContext messageContext) {
if (log_p)
logger.info("close");
}
2..Mention this class in the Handler-Chain.xml.
<javaee:handler>
<javaee:handler-class>
com.example.client.handler.VersionNumberHandler
</javaee:handler-class>
</javaee:handler>
3..Add the handler-chain in the client (Stub) also.
#WebServiceClient(name = "TicketWSImplService", targetNamespace = "http://ticket.example.com/", wsdlLocation = "http://localhost:8080/ticket?wsdl")
#HandlerChain(file = "handler-chain.xml")
public class TicketWSImplService extends Service {
#WebMethod
public void method(){
}
Here, we are adding a new header element "versionnumber" and mustunderstand=true, which means the server/intermediaries has to process this element, otherwise Jax-WS-Runtime will throw SOAP Fault exception to the client. Now we need to write a Validator(SOAP Handler) at the server side to validate this version number which is being passed by the clients.
Server Side:
1..Write a VersionNumberValidator as shown below.
public class VersionNumberValidator implements SOAPHandler<SOAPMessageContext> {
#SuppressWarnings("unused")
#Override
public boolean handleMessage(SOAPMessageContext ctx) {
// Is this an inbound message, i.e., a request?
Boolean response_p = (Boolean) ctx
.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
// Manipulate the SOAP only if it's incoming.
if (!response_p) {
try {
SOAPMessage msg = ctx.getMessage();
SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
SOAPHeader hdr = env.getHeader();
// Ensure that the SOAP message has a header.
if (hdr == null) {
generateSOAPFault(msg, "No message header.");
return true;
}
Iterator mustUnderstandHeaders = msg.getSOAPHeader()
.examineMustUnderstandHeaderElements(
"http://schemas.xmlsoap.org/soap/actor/next");
String value = null;
while (mustUnderstandHeaders.hasNext()) {
Node next = (Node) mustUnderstandHeaders.next();
System.out.println("mustUnderstandHeaders name:"
+ next.getValue());
if (next.getNodeName().equalsIgnoreCase("versionnumber"))
value = next.getValue();
if (value != null && !value.equalsIgnoreCase("v1.0")) {
generateSOAPFault(msg, "Version Number Mismatch");
}
}
// For tracking, write to standard output.
msg.writeTo(System.out);
} catch (SOAPException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
}
return true; // continue down the chain
}
#Override
public boolean handleFault(SOAPMessageContext ctx) {
return true; // do continue down the chain
}
// For now, no-ops.
#Override
public Set<QName> getHeaders() {
Set<QName> headers = new HashSet<QName>();
QName qName = new QName("http://ticket.example.com/", "versionnumber");
headers.add(qName);
return headers;
}
#Override
public void close(MessageContext messageContext) {
}
private void generateSOAPFault(SOAPMessage msg, String reason) {
try {
SOAPBody body = msg.getSOAPBody();
SOAPFault fault = body.addFault();
QName fault_name = new QName(
SOAPConstants.URI_NS_SOAP_1_2_ENVELOPE, "UltimateReceiver");
fault.setFaultCode(fault_name);
fault.setFaultRole("http://ticket.example.com/versionNumber_validator");
fault.addFaultReasonText(reason, Locale.US);
} catch (SOAPException e) {
}
}
2..Mention this class in the Handler-Chain-server.xml.
<javaee:handler>
<javaee:handler-class>
com.example.client.handler.VersionNumberValidator
</javaee:handler-class>
</javaee:handler>
3..Publish the webservices.
Now, the every client request will be having "version number =v1.0", At the server side , you will be validating this value is correct or not. If it is not correct, SOAPFaultException will be thrown.
You could add it to the http-headers but that would mean your client would need to do this which also means they can change it and give you wrong numbers causing issues on the server. It's only as reliable as the messages being sent in.
Either way, this isn't the right way to restrict access to your Web Service, you should use http basic authentication or if it's version differences then you should create multiple version endpoints giving clients access to the versions they need.
Also, JBoss 4.2.3 is so old it might not even work. See [1]
Mus
[1] https://community.jboss.org/message/534711
It's a bad idea to try to add out-of-band metadata to a web service. Just pick a new URL for each version if the data structures are incompatible. If they are compatible, put the version number inside the request.
This way you can still support interoperation with all different libraries and not require your clients to find a new hoop to jump through for each toolkit.

How to persist SOAP Messages in MySQL Database - working with Axis Client

I am writing a java axis client, how could I persist raw xml into data base, till now I have found two ways of logging raw xml, but they are for console or to a file, but I need to persist each request and response into mysql database, where I could use connection as a user parameter.
here is what I have done already.
log raw ws xml to console
log raw ws xml to a file
Well I have found a solution, First we need to use the Custom handler as I mentioned earlier(1), we can set property in the message context
like
public class FedexWsHandler extends GenericHandler {
public QName[] getHeaders() {
return null;
}
public boolean handleRequest(MessageContext context) {
try {
SOAPMessageContext smc = (SOAPMessageContext) context;
SOAPMessage reqMsg = smc.getMessage();
context.setProperty("req-msg", reqMsg);
} catch (Exception ex) {
ex.printStackTrace();
}
return true;
}
public boolean handleResponse(MessageContext context) {
try {
SOAPMessageContext smc = (SOAPMessageContext) context;
SOAPMessage reqMsg = smc.getMessage();
context.setProperty("res-msg", reqMsg);
} catch (Exception ex) {
ex.printStackTrace();
}
return true;
}
}
and then in out client we can get that property and do what ever you want to do, like
MyServiceLocator locator = new MyServiceLocator();
MyService service = locator.getMyService();
service.getResults("foo", "bar"); // call the service
// I want to get that message I have set in request and response handler methods
MessageContext ctx = locator.getCall().getMessageContext();
SOAPMessage reqMsg = (SOAPMessage) requestContext.getProperty("req-msg");
SOAPMessage resMsg = (SOAPMessage) requestContext.getProperty("res-msg");
But it is not a safe way to do that as this is not Thread Safe. as per its docs
.... so if any one can suggest me some better solution.
JAXB Marshaller : StringWriter output has a truncated tag value
Please have a look on the above post, you can convert the xml to java string and and than you can use any mysql data type which can hold your xml converted to string. you can use blob if the string in very large.

Add header to SOAP message

I need add custom soap header, like login
I do it in way like this
class Foo implements SOAPHandler<SOAPMessageContext> {
public boolean handleMessage(SOAPMessageContext context) {
try {
SOAPMessage soapMsg = context.getMessage();
SOAPEnvelope soapEnv = soapMsg.getSOAPPart().getEnvelope();
soapEnv.addHeader().addAttribute(new QName("login"), "bob");
soapMsg.writeTo(System.out);//tracing OUT
return true;
} catch (SOAPException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
#HandlerChain(file="handler-chain.xml")//I describe Foo in this file
public class GreeterService
By tracing out I get message
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Header login="bob"/><S:Body><ns2:sayGoodbye xmlns:ns2="http://example.com/"><arg0>SOAP</arg0></ns2:sayGoodbye></S:Body></S:Envelope>
with header
<S:Header login="bob"/>
But server received it without any header
<?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><ns2:sayGoodbye xmlns:ns2="http://example.com/"><arg0 xmlns="">SOAP</arg0></ns2:sayGoodbye></S:Body></S:Envelope>
What I make wrong?
I had the similar issue few days ago, there was a need to send user id by header.
I resolved this problem with special parameter - wsimport -XadditionalHeaders when generating code.

Categories

Resources