tags:

views:

322

answers:

3

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();
 }
+1  A: 

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.

Bombe
thanks you. my programme now works using synchronized
winson tan
+1  A: 

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.  

}
Nick Holt
+2  A: 

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.

hbunny
Thanks for reply. Removing thread sound problem to me, my programme need to exec dynamic number of sub process and monitor the sub process. Any idea to over come this?
winson tan
You have multiple child processes that might need to modify the same XML file on the file system? In which case, use the lock file pattern to synchronize access to the file.
hbunny