package tmcsim.paramicscommunicator; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.LinkedList; import java.util.Observable; import java.util.Timer; import java.util.TimerTask; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.xml.serialize.OutputFormat; import org.apache.xml.serialize.XMLSerializer; import org.w3c.dom.Element; import tmcsim.paramicscommunicator.FileIOUpdate.IO_TYPE; /** * The ParamicsFileWriter handles writing messages to a target file which is * read by Paramics. Messages are received through the writeMessage() method. * This object handles queueing messages and writing as the file becomes * available. New data is written to the target file when it has been modified * (cleared) by Paramics. If this does not happen, messages are queued and a * timer is used to periodically determine if the file has become available for * writing. * * @author Matthew Cechini * @version */ public class ParamicsFileWriter extends Observable { /** * Duration (in ms) that the TimerTask will be scheduled to retry writing to * the target file. Default = 2000ms */ private static long TIMER_DURATION = 2000; /** * Error Logger. */ private Logger paramLogger = Logger.getLogger("tmcsim.paramicscommunicator"); /** * Linked List of messages that have been received */ private LinkedList queuedMessages = null; /** * */ private String writerID = null; /** * File name of the file where data is written */ private String outputFile = null; /** * FileWriter used to write data to the output file. */ private FileWriter fileWriter = null; /** * Value (seconds since 1/1/1970) of output file's last modifcation time */ private long lastModified = 0; /** * Timer used to schedule file writing tasks. */ private Timer writerTimer = null; /** * Synchronizing lock to protect File IO and message queuing. */ private Object lock = null; /** * A TimerTask to retry writing messages that have been queued within this * ParamicsWriter. If a message has been queued, see if the target file has * been modified since last write. If so, write the first queued message to * the file and remove the message from the queue. If writing is * unsuccessful, do not remove the message from the queue. If there are no * more messages in the queue, cancel this timer. */ private class WriterTimerTask extends TimerTask { public void run() { synchronized (lock) { paramLogger.log(Level.INFO, "ParamicsFileWriter, WriterTimerTask " + "run() started, current queue size = " + queuedMessages.size()); //if we've queued something, continue. if (queuedMessages.size() > 0) { //if file has been modified, write to it if (lastModified < new File(outputFile).lastModified()) { try { writeToFile(queuedMessages.getFirst()); queuedMessages.remove(0); } catch (IOException ioe) { paramLogger.logp(Level.SEVERE, "ParamicsFileWriter.WriterTimerTask", "run()", "Exception in writing to the target file: " + outputFile + ". Queue size = " + queuedMessages.size(), ioe); } //all queued messages gone, cancel timer if (queuedMessages.size() == 0) { this.cancel(); } } } } } } /** * Constructor. Initialize data objects. If the target file exists, delete * it, and then create a new file. * * @param workingDir Directory path where the output file is to be written * @param mess The ParamicsCommMessage containing the outputFile filename. */ public ParamicsFileWriter(String id, String workingDir, String targetFile) { try { writerID = id; queuedMessages = new LinkedList(); lock = new Object(); outputFile = workingDir + targetFile; File tempFile = new File(outputFile); if (tempFile.exists()) { tempFile.delete(); } tempFile.createNewFile(); writerTimer = new Timer(); } catch (IOException ioe) { paramLogger.logp(Level.SEVERE, "ParamicsFileWriter", "Constructor", "Unable to create Paramics File Writer.", ioe); } } /** * Method is called when a message has been received from the CAD Simulator. * If the message queue is not empty, add the new message to the queue. If * the output file has not been modified (read) since last write, add the * message to the queue and set a timer to repeatedly check for modification * to the output file. Else, write the new message to the file. If there is * an error in writing the data, queue the message start a timer to retry * the writing. * * @param newMessage The received message which is to be written to the * output file. */ public void writeMessage(Element messageElem) { synchronized (lock) { paramLogger.log(Level.INFO, "ParamicsFileWriter, writeMessage " + "current queue size = " + queuedMessages.size()); //messages already queued... get in line. if (queuedMessages.size() > 0) { queuedMessages.add(messageElem); paramLogger.log(Level.INFO, "Queueing message, new queue " + "size = " + queuedMessages.size()); } //No modification since last write. (first queue) else if (lastModified >= new File(outputFile).lastModified()) { paramLogger.log(Level.INFO, "ParamicsFileWriter, writeMessage " + "adding msg to queue."); queuedMessages.add(messageElem); writerTimer.scheduleAtFixedRate(new WriterTimerTask(), 0L, TIMER_DURATION); paramLogger.log(Level.INFO, "First message queued"); } //free and clear, write. else { paramLogger.log(Level.INFO, "ParamicsFileWriter, writeMessage " + "try to writeToFile" + outputFile); try { writeToFile(messageElem); } catch (IOException ioe) { paramLogger.logp(Level.SEVERE, "ParamicsFileWriter", "writeMessage()", "Exception in writing to the " + "target file: " + outputFile, ioe); queuedMessages.add(messageElem); writerTimer.scheduleAtFixedRate(new WriterTimerTask(), 0L, TIMER_DURATION); } paramLogger.log(Level.INFO, "ParamicsFileWriter, writeMessage " + "writeToFile succeeded."); } } } /** * Method writes data to the output file. * * @param output Data to be written to the file. */ private void writeToFile(Element output) throws IOException { fileWriter = new FileWriter(outputFile); OutputFormat of = new OutputFormat("XML", "ISO-8859-1", true); of.setIndent(1); of.setIndenting(true); XMLSerializer serializer = new XMLSerializer(fileWriter, of); serializer.asDOMSerializer(); serializer.serialize(output); /** * Added by Nathaniel Lehrer */ try { java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream(); new XMLSerializer(outputStream, of).serialize(output); tmcsim.paramicslog.ParamicsLog.getInstance().writeToLog(outputStream.toString()); } catch (Exception e) { System.out.println(e); } /** * End Add by Nathaniel Lehrer */ fileWriter.flush(); fileWriter.close(); lastModified = new File(outputFile).lastModified(); setChanged(); notifyObservers(new FileIOUpdate(IO_TYPE.WRITE, writerID, new File(outputFile).length())); } }