Applying a xalan java transformation to an axis2 object? - java

I am tying to get the open source OWL-S API http://on.cs.unibas.ch/owls-api/ to use axis2. I have managed to get the requests sent correctly but when it comes to the response I am having trouble applying a transformation to it. In order to make my question easier to answer I am providing some standalone code which should run without having to import the project. To setup the DOMSource:
String xmlString = "<ns1:countResponse xmlns:ns1=\"http://www.test.de/pill-counter\"><ns1:value>0</ns1:value><ns1:value>0</ns1:value><ns1:value>1</ns1:value><ns1:value>0</ns1:value><ns1:value>0</ns1:value><ns1:value>0</ns1:value></ns1:countResponse>";
ByteArrayInputStream xmlStream = new ByteArrayInputStream(xmlString.getBytes());
OMElement test = null;
try {
StAXBuilder builder = new StAXOMBuilder(xmlStream);
test = (OMElement) builder.getDocument().getChildren().next();
} catch (XMLStreamException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
OMElement documentElement = null;
try {
documentElement = AXIOMUtil.stringToOM(DOOMAbstractFactory.getOMFactory(), xmlString);
} catch (XMLStreamException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
SAAJConverterFactory convFactory = (SAAJConverterFactory) FactoryRegistry.getFactory(org.apache.axis2.jaxws.message.factory.SAAJConverterFactory.class);
SAAJConverter conv = convFactory.getSAAJConverter();
//Create soap 1.1 message
SOAPMessage msg = MessageFactory.newInstance().createMessage();
SOAPPart sp = msg.getSOAPPart();
SOAPEnvelope se = sp.getEnvelope();
SOAPBody soapBody = se.getBody();
javax.xml.soap.SOAPFactory soapFactory = javax.xml.soap.SOAPFactory.newInstance();
response = conv.toSAAJ(documentElement, soapBody, soapFactory);
Node root = response;
And now to apply the transformation:
Transformer transformer = null;
try {
transformer = TransformerFactory.newInstance().newTransformer(new StreamSource(new StringReader("<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:ns1=\"http://www.test.de/pill-counter\">\n\n\t<xsl:template match=\"/\">\n\t\t<xsl:value-of select=\"sum(*/ns1:value)\" />\n\t</xsl:template>\n</xsl:stylesheet>")));
} catch (TransformerConfigurationException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
} catch (TransformerFactoryConfigurationError e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
try {
transformer.transform(new DOMSource(root), new StreamResult(System.out));
} catch (TransformerException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
The result of running this code is a NullPointerException.
SystemId unknown; Line num.0; Column num.0; java.lang.NullPointerException
I have tried searching for a solution to this problem on Google, the Xalan-j mailing list and on this site with no luck. I have also tried with several other coding approaches and no luck. Any ideas from anyone?
I found another way to get this working by generating the Document from scratch:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setValidating(false);
DocumentBuilder db = null;
try {
db = dbf.newDocumentBuilder();
} catch (ParserConfigurationException e3) {
// TODO Auto-generated catch block
e3.printStackTrace();
}
InputSource is = new InputSource(new StringReader(documentElement.toString()));
Document document = null;
try{
document=db.parse(is);
} catch (SAXException e3) {
// TODO Auto-generated catch block
e3.printStackTrace();
} catch (IOException e3) {
// TODO Auto-generated catch block
e3.printStackTrace();
}

To use the JAXP API on an Axiom tree you don't need to convert it first to SAAJ or DOM. Axiom is able to create a SAXSource that can be passed to JAXP. An example can be found here. That example uses the javax.xml.validation API, but for javax.xml.transform it works the same way.
Note that the example uses some APIs introduced in recent Axiom versions, but the feature already exists for quite some time. Depending on the Axiom version you are using, the code needs to be adapted to the older API. In particular, instead of calling getSAXSource (which was introduced in 1.2.13), you need to construct an org.apache.axiom.om.impl.jaxp.OMSource object and pass that to JAXP.

Have you considered using wsdl2java to construct stubs, rather that using the low-level API directly? This would let you easily manipulate the reponse in java. Using xslt seems like an unusual approach in this case.

Related

How to remove namespace from then signature elements in Xades4J signature?

I have the following code, that imports content.xml into document.xml and signs the document.xml root element.
try {
KeyingDataProvider kp = new FileSystemKeyStoreKeyingDataProvider(
"pkcs12",
"C:\\workspace\\tests\\my\\LG.pfx",
new FirstCertificateSelector(),
new DirectPasswordProvider("mykeypass"),
new DirectPasswordProvider("mykeypass"),
true);
XadesSigningProfile p = new XadesBesSigningProfile(kp);
XadesSigner signer = p.newSigner();
javax.xml.parsers.DocumentBuilderFactory factory = javax.xml.parsers.DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
javax.xml.parsers.DocumentBuilder builder = null;
builder = factory.newDocumentBuilder();
Document doc1 = builder.parse(new File("C:\\workspace\\tests\\document.xml"));
Document doc2 = builder.parse(new File("C:\\workspace\\tests\\content.xml"));
Node contentElement = doc2.getDocumentElement();
Node parentElement = doc1.getDocumentElement();
Node adoptedContentElement = doc1.adoptNode(contentElement);
parentElement.appendChild(adoptedContentElement);
Node nodeToSign = doc1.getDocumentElement().getFirstChild();
Node nodeToAttachSignature = doc1.getDocumentElement();
IndividualDataObjsTimeStampProperty dataObjsTimeStamp = new IndividualDataObjsTimeStampProperty();
AllDataObjsCommitmentTypeProperty globalCommitment = AllDataObjsCommitmentTypeProperty.proofOfApproval();
CommitmentTypeProperty commitment = CommitmentTypeProperty.proofOfCreation();
DataObjectDesc obj = new EnvelopedXmlObject(nodeToSign, "text/xml", null);
SignedDataObjects dataObjs = new SignedDataObjects(obj).withCommitmentType(globalCommitment);
signer.sign(dataObjs, nodeToAttachSignature);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
Result output = new StreamResult(new File("C:\\workspace\\tests\\signedDocument.xml"));
Source input = new DOMSource(doc1);
transformer.transform(input, output);
} catch (KeyStoreException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (XadesProfileResolutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TransformerConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TransformerFactoryConfigurationError e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TransformerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (XAdES4jException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
that produces the signature like:
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="xmldsig-26102a68-cfea-43fd-a40e-9682ae7da4a1">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
The question is - how to remove namespace ds: from the signature elements?
I saw a line where the prefix "ds" is set in an example named CreateSignature, for the Apache Santuario library in Java.
ElementProxy.setDefaultPrefix(Constants.SignatureSpecNS, "ds");
In the book written by the developer of the library, he specifies how to define the signature, because XAdES4j is mostly to build the signature and not the whole document:
5.2 Signature Production: as previously discussed, a signature can be seen as having two parts: the first consists of the characteristics of
the signer and the signature operation itself; the second, the
resources being signed. If the signer is seen as a regular signature
producer, he’s likely to have a set of characteristics that are used
whenever a signature is created, i.e a signature profile. These
characteristics are fixed between signatures, while the signed
resources vary. Thus, producing a signature is to combine a profile
and a set of resources in order to create the final XML structure.
This process comes down to three major tasks: gather the needed
information (signature and data objects properties, algorithms, keying
data) in appropriate order; create the core signature structure using
the Apache XML Security API; and create the qualifying properties DOM
tree to be appended to the signature. Note that Apache XML Security
creates the DOM tree for the core signature structure. However, the
XAdES elements are unknown to the Apache API, which means that the
last task has to be completely supported by the library.
cited from: XAdES4j — a Java Library for XAdES Signature Services. By Luís Filipe dos Santos Gonçalves
By "removing namespace" I think you meaning removing the prefix. This is not somethign you can control via xades4j because the default prefixes are set by Apache Santuario and then used when creating a XMLSignature. The defaults seem to be set on the Init class; I'm not sure how/if to override the settings.
Try to put into your code:
Constants.setSignatureSpecNSprefix("");
Kindly use java version 6.32 or above to avoid appending ds: in signed XML

Convert an XML String to CSV file (Java) -> CSV Result is empty

I need help understanding why the XML String does not convert into a CSV file. Why is the CSV Resut always empty?
Here is an example of a Java Code
public class transformCSV_1 {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
String xmlData = new String(
"<?xml version=\"1.0\"?><PurchaseOrder xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://localhost:8080/source/schemas/poSource/xsd/purchaseOrder.xsd\"><Reference>SBELL-2002100912333601PDT</Reference><Actions><Action><User>SVOLLMAN</User></Action></Actions><Requestor>Sarah J. Bell</Requestor><User>SBELL</User><CostCenter>S30</CostCenter><ShippingInstructions><name>Sarah J. Bell</name><address>400 Oracle Parkway Redwood Shores CA 94065 USA</address><telephone>650 506 7400</telephone></ShippingInstructions><SpecialInstructions>Air Mail</SpecialInstructions><LineItems><LineItem ItemNumber=\"1\"><Description>A Night to Remember</Description><Part Id=\"715515009058\" UnitPrice=\"39.95\" Quantity=\"2\"/></LineItem><LineItem ItemNumber=\"2\"><Description>The Unbearable Lightness Of Being</Description><Part Id=\"37429140222\" UnitPrice=\"29.95\" Quantity=\"2\"/></LineItem><LineItem ItemNumber=\"3\"><Description>Sisters</Description><Part Id=\"715515011020\" UnitPrice=\"29.95\" Quantity=\"4\"/></LineItem></LineItems></PurchaseOrder>");
String stylesheet = new String(
"<?xml version=\"1.0\" encoding=\"utf-8\"?><xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"><xsl:output method=\"text\" /><xsl:variable name=\"delimiter\" select=\"','\" /><!-- define an array containing the fields we are interested in --><xsl:variable name=\"fieldArray\"><field>Reference</field><field>User</field><field>Reject</field><field>Requestor</field></xsl:variable><xsl:param name=\"fields\" select=\"document('')/*/xsl:variable[#name='fieldArray']/*\" /><xsl:template match=\"/\"><!-- output the header row --><xsl:for-each select=\"$fields\"><xsl:if test=\"position() != 1\"><xsl:value-of select=\"$delimiter\"/></xsl:if><xsl:value-of select=\".\" /></xsl:for-each><!-- output newline --><xsl:text>
</xsl:text><xsl:apply-templates select=\"PurchaseOrder\"/></xsl:template><xsl:template match=\"PurchaseOrder\"><xsl:variable name=\"currNode\" select=\".\" /><!-- output the data row --><!-- loop over the field names and find the value of each one in the xml --><xsl:for-each select=\"$fields\"><xsl:if test=\"position() != 1\"><xsl:value-of select=\"$delimiter\"/></xsl:if><xsl:value-of select=\"$currNode/*[name() = current()]\" /></xsl:for-each><!-- output newline --><xsl:text>
</xsl:text></xsl:template></xsl:stylesheet>");
InputStream xmlSource = new ByteArrayInputStream(
xmlData.getBytes("UTF-8"));
InputStream styleSource = new ByteArrayInputStream(
stylesheet.getBytes("UTF-8"));
DocumentBuilderFactory factory = DocumentBuilderFactory
.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(xmlSource);
StreamSource stylesource = new StreamSource(styleSource);
Transformer transformer = TransformerFactory.newInstance()
.newTransformer(stylesource);
Source source = new DOMSource(document);
Result outputTarget = new StreamResult(new File("src/resultI.csv"));
transformer.transform(source, outputTarget);
} catch (TransformerConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TransformerFactoryConfigurationError e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TransformerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Best way is to use XSLT to "transform" the XML to CSV --> Can someone give me hint?
Consider to load the XSLT code from a file or URI (instead of from a string) as otherwise your approach of doing <xsl:param name="fields" select="document('')/*/xsl:variable[#name='fieldArray']/*" /> where document('') tries to pull in the XSLT code again is likely to fail, unless you set up a special resolver.

Append data to already existing Sitemap.xml file

I have created sitemap using the com.redfin.sitemapgenerator jar, now I want to add nodes i.e the urls in the urlset at run time, I have checked that the sitemap already exists or not but now I am unable to append to the already existing sitemap.xml file.`public String searchforques()
throws IllegalArgumentException {
// TODO Auto-generated method stub
System.out.println("calledd");
WebSitemapGenerator sitemapGenerator = null;
WebSitemapUrl sitemapUrl = null;
File file=new File("C:\\sitemap\\sitemap.xml");
try {
if(!file.exists())
{
sitemapGenerator = WebSitemapGenerator
.builder("http://www.testing.com", new File("C:\\sitemap"))
.gzip(false).build();
/*------------------------------------------*/
try {
sitemapUrl = new WebSitemapUrl.Options(
"http://www.testing.com/#!dashboard1")
.lastMod(new Date()).priority(1.0)
.changeFreq(ChangeFreq.HOURLY).build();
System.out.println("-----------");
System.out.println("sitemapUrl :: "+sitemapUrl.getUrl());
sitemapGenerator.addUrl(sitemapUrl);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/****************/
}
else
{
System.out.println("file already exist");
sitemapGenerator = WebSitemapGenerator
.builder("http://www.testing.com", file)
.gzip(false).build();
try {
sitemapGenerator
.addUrl("http://www.testing.com/#!ques");
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("sitemapGenerator :: "+sitemapGenerator);
}
} catch (MalformedURLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
sitemapGenerator.write();
// this will configure the URL with lastmod=now, priority=1.0,
// changefreq=hourly
// You can add any number of urls here
return "";
}
But my else part is not working it directly goes to server fail.
I am working on java with gwt.
So, any one could help to add dynamically the nodes(urls) in the urlset at runtime.
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
File file=new File("war/abc.xml"); //local war
String val=null;
if(!file.exists())
{
val= "not exist";
try {
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document document = docBuilder.newDocument();
Element rootElement = document.createElement("urlset");
rootElement.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns", "http://www.sitemaps.org/schemas/sitemap/0.9");
document.appendChild(rootElement);
Element url = document.createElement("url");
Element loc = document.createElement("loc");
loc.appendChild(document.createTextNode(urllink));
url.appendChild(loc);
Element lastmod = document.createElement("lastmod");
lastmod.appendChild(document.createTextNode(dateText));
url.appendChild(lastmod);
Element changefreq = document.createElement("changefreq");
changefreq.appendChild(document.createTextNode("always"));
url.appendChild(changefreq);
Element priority = document.createElement("priority");
priority.appendChild(document.createTextNode("1.0"));
url.appendChild(priority);
rootElement.appendChild(url);
// write the content into xml file
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = null;
try
{
transformer = transformerFactory.newTransformer();
}
catch (TransformerConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(file);
// Output to console for testing
//StreamResult result = new StreamResult(System.out);
try
{
transformer.transform(source, result);
}
catch (TransformerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("File saved!");
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else
{
System.out.println("file already exist append ");
val="file already exist append ";
DocumentBuilder documentBuilder = null;
try {
documentBuilder = docFactory.newDocumentBuilder();
} catch (ParserConfigurationException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
Document document = null;
try {
document = documentBuilder.parse(file);
document.normalize();
} catch (SAXException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
} catch (IOException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
Element rootElement = document.getDocumentElement();
Element url = document.createElement("url");
Element loc = document.createElement("loc");
loc.appendChild(document.createTextNode(urllink));
url.appendChild(loc);
Element lastmod = document.createElement("lastmod");
lastmod.appendChild(document.createTextNode(dateText));
url.appendChild(lastmod);
Element changefreq = document.createElement("changefreq");
changefreq.appendChild(document.createTextNode("always"));
url.appendChild(changefreq);
Element priority = document.createElement("priority");
priority.appendChild(document.createTextNode("1.0"));
url.appendChild(priority);
rootElement.appendChild(url);
DOMSource source = new DOMSource(document);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = null;
try {
transformer = transformerFactory.newTransformer();
} catch (TransformerConfigurationException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
StreamResult result = new StreamResult(file);
System.out.println("result :: "+result.toString());
try {
transformer.transform(source, result);
} catch (TransformerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

Java parse XML string

I have an string which I am attempting to extract values from, for convenience I thought that converting the string to a Document and then parsing the xml would be the best way to do this but I am running into all sorts of problems! The string looks like:
<Messagexxx>
<Unit>
<contact>0</contact>
<text>Test Content</text>
<date>09-Sep-14 13:56</date>
<subject>Test Title</subject>
</Unit>
</Messagexxx>
Can someone point me in the correct way to achieve my goal of reading the values from the tags .
I have attempted using the following snippet but I all the values in the array are
null! Document xml = null; Node T = null; try { xml = stringToDom(message); T = xml.getLastChild(); } catch (SAXException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ParserConfigurationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } if(xml.getFirstChild() != null){ }
When you write your string to a text file, you can first parse it:
private Document parse(String filename){
Document doc = null;
try {
DOMParser parser = new DOMParser();
parser.parse(filename);
doc = parser.getDocument();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return doc;
}
and then you read all text elements out of this document:
public void extract (Document doc){
Node root = doc.getDocumentElement();
for (int i = 0; i< root .getChildNodes().getLength(); i++){
Node child = root.getChildNodes().item(i);
System.out.println(child.getTextContent());
}
}
Use JAXB lib : https://jaxb.java.net/
Create your model from your XML and to read :
JAXBContext jaxbContext = JAXBContext.newInstance(YourModel.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader("xml string here");
YourModel yourModel= (Person) unmarshaller.unmarshal(reader);
After your can use the object "YourModel" to read your value.
This is a very simple way to get node values when you know the node names, and they don't repeat:
String getXmlNodeValue(String xmlString, String nodeName){
int start = xmlString.indexOf("<"+nodeName+">") + nodeName.length() + 2;
int end = xmlString.indexOf("</"+nodeName+">");
return xmlString.substring(start, end);
}

Java parse and update xml value in thread

I written a multiple thread program, each thread need to parse a xml file and update new value. The problem: assuming i have a xml contents of ABC, now thread A parse the xml and update it become ABCA, at the same time thread B also parse the xml (which the content is ABC) and update it become ABCB. thread B update the xml after thread A updated, so the result of the xml is ABCB, what i want is the xml result should be ABCAB. Any idea to control the way of parsing and update in the thread?
here's my code:
WSthread.java
public class WSthread extends Thread {
public String WSname;
Process proc= null;
WebServicesXML xml;
WSthread(String name){
WSname=name;
}
public void run() {
try {
//my code
// Run a java app in a separate system process
String cmd = (WSname);
proc = Runtime.getRuntime().exec("java -jar "+cmd);
xml = new WebServicesXML();
//Process proc = Runtime.getRuntime().exec("java -jar .jar");
// Then retreive the process output
//InputStream in = proc.getInputStream();
//InputStream err = proc.getErrorStream();
BufferedReader is = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line;
String regex = "\\bhttp\\b";
Pattern pattern = Pattern.compile(regex);
String WSaddress = "";
while ((line = is.readLine()) != null){
Matcher matcher = pattern.matcher(line);
if(matcher.find()){
WSaddress = line;
System.out.println("Updating WS address..."+WSaddress);
xml.create(WSname, WSaddress);
}
System.out.println(line);
}
}catch(Exception e){
System.out.println(e.getMessage());
}
}
public void close(){
proc.destroy();
}
WebServicesXML.java
public class WebServicesXML{
public int totalWebServices;
public String WSnamelist[];
public synchronized void create(String WSname, String WSaddress) throws OException, TransformerConfigurationException, TransformerException, ParserConfigurationException{
try {
DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
Document readdoc = docBuilder.parse("webservices.xml");
// normalize text representation
readdoc.getDocumentElement ().normalize ();
Node rootList = readdoc.getDocumentElement();
Element rootElement = (Element)rootList;
Element webservice = readdoc.createElement("WebService");
Element name = readdoc.createElement("name");
Element address = readdoc.createElement("address");
name.setTextContent(WSname);
address.setTextContent(WSaddress);
webservice.appendChild(name);
webservice.appendChild(address);
rootElement.appendChild(webservice);
/////////////////
//Output the XML
//set up a transformer
TransformerFactory transfac = TransformerFactory.newInstance();
Transformer trans = transfac.newTransformer();
trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
trans.setOutputProperty(OutputKeys.INDENT, "yes");
//create string from xml tree
File file = new File("webservices.xml");
StringWriter sw = new StringWriter();
StreamResult result = new StreamResult(file);
DOMSource source = new DOMSource(readdoc);
trans.transform(source, result);
}
catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TransformerConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TransformerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
The basic problem is a seperate DOM is built in every thread for a single transform to be applied. This means that the last thread to run 'wins' in terms of writing its content back to the XML file.
On the one hand you are using threads, I assume for performance, but on the other hand you parse and serialize the XML multiple times. And the threading implementation is unsafe.
My recommendation is to remove the threading and do the changes in loop. When it's working you can measure the performance and THEN choose to look at an implementation using threads.
Remember, premature optimization is the root of all evil.
You need to make sure that only one thread is accessing the file at any time. You can use the native synchronization tools available for that (such as the synchronized keyword and the wait()/notify() mechanism) or you can look into higher-level synchronization tools such as Semaphore or FileLock.
When you synchronize a method the lock is taken on the object (for non-static methods) so because each thread gets its own instance of WebServicesXML each can obtain a lock and no blocking occurs.
The easiest way to deal with this would be to only create a single instance of WebServicesXML and pass that single instance to each thread when it is created.
Alternatively create a semaphore object in WebServicesXML like this:
private static final Object FILE_SEMAPHORE = new Object();
And then add a synchronized block round the lines that update the file like this:
synchronized (FILE_SEMAPHORE) {
//File update code goes here.
}

Categories