package tmcsim.paramicslog;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.rmi.Naming;
import java.util.Observable;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import org.w3c.dom.Element;
import tmcsim.common.SimulationException;
import tmcsim.interfaces.CoordinatorInterface;
import tmcsim.paramicslog.gui.ParamicsLogGUI;
/**
* Logs communication from ParamicsCommunicator to ParamicsSimulator.
*
* The system property "PARAMICS_LOG_CONFIG" should be set to the path
* where the properties file for this class is located.
* The data for the properties file follows.
*
* -----------------------------------------------------------------
* Log File The file to write the communication to.
* CAD Simulator Host The host that runs the CAD Simulator.
* CAD Simulator RMI Port The port on the host that runs the CAD
* Simulator where the RMI Coordinator
* object is registered to.
* -----------------------------------------------------------------
* Example File:
* LogFile=c:\\log.txt
* CADSimulatorHost=localhost
* CADSimulatorRMIPort=4445
* -----------------------------------------------------------------
*
*
* @author Nathaniel Lehrer
* @version
*/
public class ParamicsLog extends Observable
{
/**
* Enmeration containing property names.
* @author Nathaniel Lehrer
*/
private enum PROPERTIES
{
LOG_FILE ("LogFile"),
CAD_SIM_HOST ("CADSimulatorHost"),
CAD_SIM_PORT ("CADSimulatorRMIPort");
public String name;
private PROPERTIES(String n)
{
name = n;
}
}
/** Error logger. */
private static Logger paramLogger = Logger.getLogger("tmcsim.paramicslog");
/** Static instance. */
private static ParamicsLog instance;
/** Properties object. */
private Properties paramicsLogProp;
/** File log entries are written to */
private File logFile;
/** Stores the log entries. This contains the same information logFile. */
private StringBuilder log;
/** Remote reference to the simulation */
private CoordinatorInterface theCoorInt;
/** Object for synchronizing IO */
Object lock;
/**
* Creates the singleton instance of this class.
*/
static
{
try
{
if (System.getProperty("PARAMICS_LOG_PROPERTIES") != null)
{
instance = new ParamicsLog(System.getProperty("PARAMICS_LOG_PROPERTIES"));
}
else
{
throw new Exception("PARAMICS_LOG_PROPERTIES system property not defined.");
}
}
catch (Exception e)
{
instance = new ParamicsLog();
paramLogger.logp(Level.WARNING, "ParamicsLog", "static initializer",
"Error occured initializing application", e);
JOptionPane.showMessageDialog(null, e.getMessage(),
"Error - ParamicsLog will not save log to file.",
JOptionPane.ERROR_MESSAGE);
}
instance.addObserver(ParamicsLogGUI.getInstance());
}
/**
* Creates an instance of ParamicsLog that does not write to a file when
* writeToLog is called.
*/
private ParamicsLog() {
lock = new Object();
log = new StringBuilder("");
}
/**
* Creates an instance of ParamicsLog that writes to a file when writeToLog is called.
* @param propertiesFile
*/
private ParamicsLog(String propertiesFile) {
this();
String logFile = null;
String CADSIMHost = null;
String CADSIMPort = null;
try {
paramicsLogProp = new Properties();
paramicsLogProp.load(new FileInputStream(propertiesFile));
if ((logFile = paramicsLogProp.getProperty(PROPERTIES.LOG_FILE.name)) == null)
{
throw new Exception("Properties file missing log file location.");
}
else if ((CADSIMHost = paramicsLogProp.getProperty(PROPERTIES.CAD_SIM_HOST.name)) == null)
{
throw new Exception("Properties file missing CAD Simulator host.");
}
else if ((CADSIMPort = paramicsLogProp.getProperty(PROPERTIES.CAD_SIM_PORT.name)) == null)
{
throw new Exception("Properties file missing CAD Simulator RMI port.");
}
try
{
connect(CADSIMHost, CADSIMPort);
}
catch (Exception e)
{
JOptionPane.showMessageDialog(null,
"ParamicsLog: Could not connect to remote Coordinator object.",
"Network Error", JOptionPane.ERROR_MESSAGE);
}
try
{
createLogFile(logFile);
}
catch (Exception e)
{
JOptionPane.showMessageDialog(null,
"ParamicsLog: Could not create new log file.",
"File Error", JOptionPane.ERROR_MESSAGE);
}
} catch (Exception e) {
paramLogger.logp(Level.WARNING, "ParamicsLog", "ParamicsLog constructor",
"Properties file incorrect or missing.", e);
JOptionPane.showMessageDialog(null,
"ParamicsLog: Properties file invalid.",
"Invalid Configuration", JOptionPane.ERROR_MESSAGE);
}
}
/**
* Creates the log file.
* @param filePath The path to the file including the file name.
* @throws IOException If the log file could not be created.
*/
private void createLogFile(String filePath) throws IOException
{
try {
logFile = new File(filePath);
if (logFile.exists()) {
logFile.delete();
}
logFile.createNewFile();
} catch (Exception e) {
logFile = null;
paramLogger.logp(Level.WARNING, "ParamicsLog", "ParamicsLog constructor",
"Could not create new log file.", e);
throw new IOException("Could not create log file.");
}
}
/**
* Connect to the Coordinator's RMI object.
* @param hostname Host name of the CAD Simulator.
* @param portNumber Port number of the CAD Simulator RMI communication.
* @throws SimulationException if there is an error creating the RMI connection.
*/
private void connect(String hostname, String portNumber)
throws SimulationException {
String coorIntURL = "";
try {
coorIntURL = "rmi://" + hostname + ":" + portNumber + "/coordinator";
theCoorInt = (CoordinatorInterface)Naming.lookup(coorIntURL);
}
catch (Exception e)
{
paramLogger.logp(Level.WARNING, "ParamicsLog",
"establishRMIConnection", "Unable to establish RMI " +
"communication with the CAD Simulator. URL <" + coorIntURL + ">", e);
}
}
/**
* Accessor to the entries in the log. No file IO is used.
* @return The entries in the log.
*/
public String getLog() {
return log.toString();
}
/**
* Writes an entry to the log.
* The simulator time when the message was sent is prepended to the entry.
* Entries are padded by a blank line before and after them.
* TODO: Ensure output is written in order of request.
* @param entry
*/
public void writeToLog(String entry) {
String time = "?";
String formattedEntry;
if (theCoorInt != null)
{
try
{
time = formatTime(theCoorInt.getCurrentSimulationTime());
}
catch (Exception e)
{
paramLogger.logp(Level.WARNING, "ParamicsLog",
"RMICommunication", "Unable to communicate with RMI object", e);
}
}
formattedEntry = "\n" + "\n" + entry + "\n";
log.append(formattedEntry);
if (logFile != null)
{
try
{
synchronized(lock)
{
FileWriter writer = new FileWriter(logFile, true);
writer.append(formattedEntry);
writer.flush();
writer.close();
}
}
catch (IOException e)
{
paramLogger.logp(Level.WARNING, "ParamicsLog", "writeToLog",
"Could not write to log file.", e);
}
}
setChanged();
notifyObservers(entry);
}
/**
* Formats the time given in seconds to hh:mm:ss format.
* @param time The time in seconds.
*/
private String formatTime(long time)
{
long seconds = time % 60;
long minutes = (time - seconds) / 60;
long hours = (time - seconds - minutes * 60) / 60;
return padr(hours) + ":" + padr(minutes) + ":" + padr(seconds);
}
private String padr(long n)
{
if (n < 10)
{
return "0" + n;
}
{
return "" + n;
}
}
/**
* Accessor for static instance of this class.
* @return The instance of this class.
*/
public static ParamicsLog getInstance() {
return instance;
}
}