2013-09-21 2 views
1

У меня есть следующий способ сохранения новых данных в XML-файле. Он хранит историю чата:Безопасная запись Java в файл xml?

public void addMessage(String from, String agentName, String msg, String time, String channel){ 

    try { 
     DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); 
     DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); 
     org.w3c.dom.Document doc = docBuilder.parse(filePath); 

     Node data = doc.getFirstChild(); 

     org.w3c.dom.Element root = doc.createElement(channel); 
     org.w3c.dom.Element message = doc.createElement("message"); 
     org.w3c.dom.Element _sender = doc.createElement("sender"); _sender.setTextContent(from); 
     org.w3c.dom.Element _content = doc.createElement("content"); _content.setTextContent(msg); 
     org.w3c.dom.Element _recipient = doc.createElement("recipient"); _recipient.setTextContent(agentName); 
     org.w3c.dom.Element _time = doc.createElement("time"); _time.setTextContent(time); 


     message.appendChild(_sender); message.appendChild(_content); message.appendChild(_recipient); message.appendChild(_time); 
     root.appendChild(message); 

     data.appendChild(root); 

     TransformerFactory transformerFactory = TransformerFactory.newInstance(); 
     Transformer transformer = transformerFactory.newTransformer(); 
     DOMSource source = new DOMSource(doc); 
     StreamResult result = new StreamResult(new File(filePath)); 
     transformer.transform(source, result); 

    } 
     catch(Exception ex){ 
    System.out.println("Exceptionmodify xml"); 
    } 
} 

Проблема все вдруг я получаю исключение Exceptionmodify xml в настоящее время trown. Я предполагаю, что это связано с тем, что я получаю доступ к этому методу из нескольких разных потоков, и он перепутал xml-file.

Любые идеи, как я мог бы сделать эту тему безопасной?

+2

Не думаю. Распечатайте stacktrace исключения и убедитесь, что вы решаете правильную проблему ... – meriton

+0

Непонятный источник проблемы. Покажите нам стек. – Archer

ответ

3

Вы должны synchronize ваши методы.

Вот пример:

public class SynchronizedCounter { 
    private int c = 0; 

    public synchronized void increment() { 
     c++; 
    } 

    public synchronized void decrement() { 
     c--; 
    } 

    public synchronized int value() { 
     return c; 
    } 
} 
+0

Как бы это сделать по моему методу выше? – Alosyius

+0

Просто используйте ключевое слово «synchronized» в объявлении метода. – VWeber

+0

Синхронизированный имеет некоторые неприятные побочные эффекты - все вызывающие должны ждать, пока это их очередь. Поскольку это метод, который ничего не возвращает, я думаю, что некоторое решение, похожее на очередь, будет лучше соответствовать. – extraneon

2

Использование синхронизирована метод. как показано ниже

public synchronized void addMessage(String from, String 
          agentName, String msg, String time, String channel) 
{ 
    ..... 
} 
1

Кажется, что все сообщения записываются в один файл. Доступ к файлу должен быть однопоточным, чтобы различные сообщения не смешивались.

Как показали другие ответы, обозначение всех методов, которые обеспечивают доступ к этому файлу с синхронизированным решением. И это может быть правильным решением, если addMessage действительно что-то вернет.

Но так как addMessage ничего не возвращает, абонентам не нужно ждать, пока настанет их очередь написать сообщение.

В этом случае образец производителя-потребителя может быть более уместным. Обычно это реализуется при наличии очереди для всех производителей (тех, кто вызывает addMessage), и одного потока, который читает из этой очереди и записывает сообщение в файл.

Прекрасная реализация очереди будет: BlockingQueue. Проверьте javadoc, поскольку он показывает логику производителя-потребителя!

Если вы изменили текущую реализацию в реализации на основе очередей с наименьшим количеством работы, что бы что-то вроде этого:

Объект содержит информацию для сообщения XML, поэтому он может ставить в очередь:

public class MessageInfo { 
    private String from; 
    private String agentName; 
    private String msg; 
    private String time; 
    private String channel; 
    public MessageInfo(String from, String agentName, String msg, String time, String channel) { // this.from = from; // etc. 
    } 

    // getters 
} 

Ваш класс, с addMessage изменилось, так что помещает данные в очередь

public class Yours { 
    private Queue<MessageInfo> messageQueue; 

    public Yours(Queue<MessageInfo> queue) {this.messageQueue = queue;} 

    public void addMessage(...) { 
     MessageInfo info = new MessageInfo(...); 
     try { 
      messageQueue.offer(info); 
     } catch (InterruptedException e) { 
      System.out.println("Could not put message on queue " + info); 
     } 
    } 
} 

класс, который читает сюда m очередь и записывает в файл. Может быть только 1 на файл, поэтому возможно, что имя файла должно быть частью конструктора.

public class MessageWriter implements Runnable { 
    private Queue<MessageInfo> messageQueue; 
    public MessageWriter(Queue queue) { this.messageQueue = queue; } 
    public void run() { 
     try { 
      while(true) { consume(queue.take()); } 
      } catch (InterruptedException ex) { ... handle ... 
      } 
    } 
    void consume(MessageInfo info) { 
     // your writing logic, moved from addMessage to here 
    } 
} 

И положить его вместе:

public class Main { 
    public static void main(String[] args) { 
     // 20 messages may be on the queue to handle spikes in demand 
     // after that, offer() will wait until there is room for the next 
     Queue queue = new ArrayBlockingQueue<MessageInfo>(20); 

     Yours = new Yours(queue); 
     MessageWriter consumer = new MessageWriter(queue); 

     // Start the consumer 
     new Thread(consumer).start(); // Perhaps an executor service would be better 

     // Start your threads the way you did before 
     ... 

    } 
} 
Смежные вопросы