I'm using the Provider implementation of java-ws and wish to log the XML request prior to attempting to work with it, preferably using log4j.
I've tried using TransformerFactory and stdout to log the incoming raw XML (below), which works, but when I do, the Source object can then no longer be used and generates NULL errors beyond the logging.
I'm assuming this is because it's a stream object and can only be used once.
private void printSource(Source source) {
try {
System.out.println("==========RESPONSE============");
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
transformer.transform(source, new StreamResult(System.out));
System.out.println("\n==============================");
}
catch(Exception e) {
System.out.println(e.getMessage());
}
}
Related
I have a web service using JAX-WS API (a soap web service).
import javax.jws.WebService;
#WebService
public class Accounts {
public String getFullName(String userID){
AccountManager GFN = new AccountManager();
return GFN.getFullName(userID);
}
what can I do to change it's encoding to "UTF-8" (for non-English character)?
I fount something like "#Produces("text/html; charset=UTF-8")" but it is for JAX-RS (restful web service) i need something for JAX-WS.
Thanks.
To start, you need to get a hold of the SOAP message as early as possible. The best place to start is a javax.xml.ws.handler.LogicalHandler (as opposed to the more common type of handler, SOAPHandler).
The LogicalHandler intercepts the SOAP payload before the SOAPHandler, so it's the ideal place to do this
Within this handler, you have the liberty to whatever you want with the message, long before the encoding becomes a problem. Your code should look something like this
public class YourHandler implements LogicalHandler{
public boolean handleMessage(LogicalMessageContext context) {
boolean inboundProperty= (boolean)context.get(MessageContext.MESSAGE_INBOUND_PROPERTY);
if (inboundProperty) {
LogicalMessage lm = context.getMessage();
Source payload = lm.getPayload();
Source recodedPayload = modifyEncoding(payload); //This is where you change the encoding. We'll talk more about this
lm.setPayload(recodedPayload) //remember to stuff the payload back in there, otherwise your change will not be registered
}
return true;
}
}
So now you have the message. How to handle the change of encoding can be tricky, but it's completely up to you.
You have the option of setting the encoding on the entire message, or navigating (with xpath) to the field you're interested in and manipulating just that. Even for those two options, there are several ways of accomplishing both. I'm going to go the lazy route: set the encoding on the entire payload:
private Source modifyEncoding(Source payload){
StringWriter sw = new StringWriter();
StreamSource newSource = null;
try {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //this determines the outcome of the transformation
StreamResult output = new StreamResult(sw);
transformer.transform(source, output);
StringReader sReader = new StringReader(sw.toString());
newSource = new StreamSource(sReader);//Stuff the re-encoded xml back in a Source
} catch(Exception e){
ex.printStackTrace();
}
return newSource;
}
After the LogicalHandler, you now have the SOAPHandler. Here, setting the encoding is more straightforward, but its behaviour is implementation-dependent. Your SOAPHandler can look like this:
public class YourSOAPHandler implements SOAPHandler{
public boolean handleMessage(SOAPMessageContext msgCtxt){
boolean inbound = (boolean)msgCtxt.get(MessageContext.MESSAGE_INBOUND_PROPERTY);
if (inbound){
SOAPMessage msg = msgCtxt.getMessage();
msg.setProperty(javax.xml.soap.SOAPMessage.CHARACTER_SET_ENCODING,"UTF-8");
msgCtxt.Message(msg); //always put the message back where you found it.
}
}
return true;
}
I have researched on the subject but couldn't find any relevant info regarding that
Do we need to take any security measurements to secure javax.xml.transform.Transformer against XML external entity attacks?
I did the following and it seems to expand the dtd.
String fileData = "<!DOCTYPE acunetix [ <!ENTITY sampleVal SYSTEM \"file:///media/sample\">]><username>&sampleVal;</username>";
TransformerFactory transformerFactory = TransformerFactory.newInstance();
transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Transformer transformer = transformerFactory.newTransformer();
StringWriter buff = new StringWriter();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(new StreamSource(new StringReader(fileData)), new StreamResult(buff));
System.out.println(buff.toString());
output contains the value from the file
<username>test</username>
Your code seems correct. When I run this slightly modified JUnit test case:
#Test
public void test() throws TransformerException, URISyntaxException {
File testFile = new File(getClass().getResource("test.txt").toURI());
assertTrue(testFile.exists());
String fileData = "<!DOCTYPE acunetix [ <!ENTITY foo SYSTEM \"file://" +
testFile.toString() +
"\">]><xxe>&foo;</xxe>";
TransformerFactory transformerFactory = TransformerFactory.newInstance();
System.out.println(transformerFactory.getClass().getName());
transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Transformer transformer = transformerFactory.newTransformer();
StringWriter buff = new StringWriter();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(new StreamSource(new StringReader(fileData)), new StreamResult(buff));
assertEquals("<xxe>&foo;</xxe>", buff.toString());
}
I get the following output:
com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
[Fatal Error] :1:182: External Entity: Failed to read external document 'test.txt', because 'file' access is not allowed due to restriction set by the accessExternalDTD property.
ERROR: 'External Entity: Failed to read external document 'test.txt', because 'file' access is not allowed due to restriction set by the accessExternalDTD property.'
From the setFeature JavaDocs:
All implementations are required to support the XMLConstants.FEATURE_SECURE_PROCESSING feature. When the feature is:
true: the implementation will limit XML processing to conform to implementation limits and behave in a secure fashion as defined by the implementation. Examples include resolving user defined style sheets and functions. If XML processing is limited for security reasons, it will be reported via a call to the registered ErrorListener.fatalError(TransformerException exception). See setErrorListener(ErrorListener listener).
That error goes away if I comment out transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); and then the test fails because the entity is resolved.
Try adding an ErrorListener to both the TransformerFactory and Transformer:
transformerFactory.setErrorListener(new ErrorListener() {
#Override
public void warning(TransformerException exception) throws TransformerException {
System.out.println("In Warning: " + exception.toString());
}
#Override
public void error(TransformerException exception) throws TransformerException {
System.out.println("In Error: " + exception.toString());
}
#Override
public void fatalError(TransformerException exception) throws TransformerException {
System.out.println("In Fatal: " + exception.toString());
}
});
Transformer transformer = transformerFactory.newTransformer();
transformer.setErrorListener(transformerFactory.getErrorListener());
I see the following new console output now:
In Error: javax.xml.transform.TransformerException: External Entity: Failed to read external document 'test.txt', because 'file' access is not allowed due to restriction set by the accessExternalDTD property.
Maybe your implementation is treating it as a warning? Otherwise, maybe it's the implementation you're using? It looks like the JavaDoc spec isn't precise, so one implementation might do something different than another. I'd be interested to know faulty implementations!
I know that this is an old post but for those who find themselves here, I hope is helps :)
After applying the solution below, SonarQube still complained with 'Disable access to external entities in XML parsing' security issue :(
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
Eventually I landed on the solution below which finally fixed the issue for me.
TransformerFactory factory = TransformerFactory.newInstance();
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
I got a xslt transformation done with something like this:
public static String transform(Source xml, String xsltPath) {
try {
InputStream is = MyClass.class.getResourceAsStream(xsltPath);
final Source xslt = new StreamSource(is);
final TransformerFactory transFact = TransformerFactory.newInstance();
final Transformer trans = transFact.newTransformer(xslt);
final OutputStream os = new ByteArrayOutputStream();
final StreamResult result = new StreamResult(os);
trans.transform(xml, new StreamResult(os));
final String theResult = result.getOutputStream().toString();
return theResult;
}
catch (TransformerException e) {
return null;
}
}
As you can see xslt is loaded from resources. The function together with the transformation files i need are bundled in a library and this works as long as the library is stand alone from a main method or so.
However if this library is bundled with a webapplication and deployed in Jetty/Tomcat it gets a bit complicated. As long as the transformation files in it self do not reference any other files from resources there is no problem but with files like this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet>
<xsl:import href="import_file1.xsl" />
<xsl:import href="import_file2.xsl" />
<xsl:template name="aTtemplate">
<xsl:for-each select="document('import_file3.xml')">
...
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The imports cannot be resolved and the document from the for each loop cannot be found. In Tomcat a workaround is to put the files inside the $TOMCAT/bin directory but that is not a suitable solution for us. Is there any method to get this resources recursively out of the lib?
I'm facing an issue with JDK (both 1.6 and 1.7) XSLT transformations.
The thing is that I want to process simple WSDL that is using xsd:import for its XSD (that lies in same location) with my XSLT transformation.
public static void main(String[] args) throws Exception {
InputStream xmlStream = new FileInputStream("/home/d1x/temp/xslt/test.wsdl");
String xmlSystemId = "file:///home/d1x/temp/xslt/test.wsdl";
InputStream xsltStream = XsltTransformation.class.getResourceAsStream("wsdl-viewer.xsl");
OutputStream outputStream = new FileOutputStream("/home/d1x/temp/xslt/output.html");
new XsltTransformation().transform(xmlStream, xmlSystemId, xsltStream, outputStream);
}
public void transform(InputStream xmlStream, String xmlSystemId, InputStream xsltStream, OutputStream outputStream) {
Source xmlSource = new StreamSource(xmlStream, xmlSystemId);
Source xsltSource = new StreamSource(xsltStream);
TransformerFactory transFact = TransformerFactory.newInstance();
try {
Transformer trans = transFact.newTransformer(xsltSource);
trans.transform(xmlSource, new StreamResult(outputStream));
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}
}
When I run my code, I get this exception that is kinda hard to debug. When I remove the import, everything works fine.
Caused by: java.lang.ArrayIndexOutOfBoundsException: -1
at com.sun.org.apache.xml.internal.utils.SuballocatedIntVector.elementAt(SuballocatedIntVector.java:438)
at com.sun.org.apache.xml.internal.dtm.ref.DTMDefaultBase._firstch(DTMDefaultBase.java:524)
at com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl.access$200(SAXImpl.java:76)
at com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl$NamespaceChildrenIterator.next(SAXImpl.java:1433)
at com.sun.org.apache.xalan.internal.xsltc.dom.StepIterator.next(StepIterator.java:111)
at com.sun.org.apache.xalan.internal.xsltc.dom.StepIterator.next(StepIterator.java:111)
at com.sun.org.apache.xalan.internal.xsltc.dom.DupFilterIterator.setStartNode(DupFilterIterator.java:96)
at com.sun.org.apache.xalan.internal.xsltc.dom.UnionIterator$LookAheadIterator.setStartNode(UnionIterator.java:78)
at com.sun.org.apache.xalan.internal.xsltc.dom.MultiValuedNodeHeapIterator.setStartNode(MultiValuedNodeHeapIterator.java:212)
at com.sun.org.apache.xalan.internal.xsltc.dom.CurrentNodeListIterator.setStartNode(CurrentNodeListIterator.java:153)
at com.sun.org.apache.xalan.internal.xsltc.dom.CachedNodeListIterator.setStartNode(CachedNodeListIterator.java:55)
at GregorSamsa.topLevel()
... etc...
WSDL itself is very simple and is using the import:
...<types>
<xsd:schema>
<xsd:import namespace="http://mytest.com" schemaLocation="test.xsd"/>
</xsd:schema>
</types>...
Used XSLT can be found at: http://tomi.vanek.sk/xml/wsdl-viewer.xsl
I managed to solve this issue by switching to Saxon implementation of JAXP instead of built-in Java implementation. The only code change was:
TransformerFactory transFact = net.sf.saxon.TransformerFactoryImpl.newInstance();
I use a java application to call a xslt to do a xml transformation. The xslt file will generate a message and terminate the process if some condition happens. However, my java application couldn't catch the error message generated by xslt, it only catch an exception with general information - "Stylesheet directed termination".
Here is my java code:
SAXTransformerFactory saxTFactory = ((SAXTransformerFactory) tFactory);
// Create a TransformerHandler for stylesheet.
File f2 = new File(styleSheetPath);
TransformerHandler tHandler2 = saxTFactory.newTransformerHandler(new StreamSource(f2));
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setContentHandler(tHandler2);
reader.setProperty("http://xml.org/sax/properties/lexical-handler", tHandler2);
CharArrayWriter outputWriter = new CharArrayWriter();
Result result = new StreamResult(outputWriter);
tHandler2.setResult(result);
try
{
reader.parse(new InputSource(new StringReader(XMLinput)));
}
catch(Exception ee)
{
dsiplay(ee.getMessage())
throw ee;
}
How can I catch the error message from xslt?
I tried to write a class:
private class MyErrorHandler extends DefaultHandler{
public void error(SAXParseException e)
{
System.out.println("error method "+e.getMessage());
}
public void fatalError(SAXParseException e)
{
System.out.println("fatal error method "+e.getMessage());
}
public void warning(SAXParseException e)
{
System.out.println("warning method "+e.getMessage());
}
and
MyErrorHandler myHandler = new MyErrorHandler();
reader.setErrorHandler(myHandler);
It didn't work.
Do you have any suggestion?
It looks as if you have placed an error handler on the source document XML parser, but are trying to catch errors from the transformer. So place the error handler on the Transformer rather than the XMLReader
tHandler2.getTransformer().setErrorListener(myHandler);
IIRC, I've always kept it simple and used Transformer directly rather than go for ContentHandler/TranformerHandler.
As alternatives you could go for an implementation specific extension or cause a special URI to be resolved and handle that (although as XSLT is a functional language, I guess it could technically resolve a URI that wont actually impact the result).
In what way is the XSLT generating an error and terminating? This might be crucial since the
<xsl:message>
tag does not always write to the standard output like you might expect. Especially with Saxon.
Thank you so much for the replying. Here is my xslt code:
<xsl:template match="*[not(*) and ((text() and not(normalize-space(text()) != '')) or .='')
and name(.) != 'para' and name(.) != 'recordDelimiter' and name(.) != 'physicalLineDelimiter' and name(.) != 'fieldDelimiter' ]">
<xsl:message terminate="yes">
<xsl:call-template name="output_message3_fail">
<xsl:with-param name="current_node" select="name(.)"/>
</xsl:call-template>
</xsl:message>
</xsl:template>
<xsl:template name="output_message3_fail">
<xsl:param name="current_node"/>
<xsl:text>
Conversion to EML2.1 failed:
The EML 2.0.1 document has empty string value in some elements which shouldn't be empty string in EML 2.1.0 sepcification - </xsl:text>
<xsl:value-of select="$current_node"/>
</xsl:template>
I use <xsl:message> to generate the error message.