/* ========================
* JSynoptic : a free Synoptic editor
* ========================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 2001-2005, by :
* Corporate:
* EADS Astrium SAS
* EADS CRC
* Individual:
* Claude Cazenave
*
* $Id: Viewer.java,v 1.7 2006/09/29 08:26:23 booba_skaya Exp $
*
* Changes
* -------
* 11 sept. 06 : Initial public release (CC);
*
*/
package simtools.logging.ui;
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import simtools.logging.LoggingEntryByteBuffer;
import simtools.ui.CustomizedLocale;
import simtools.ui.MenuResourceBundle;
import simtools.ui.ResourceFinder;
import simtools.ui.UserProperties;
import simtools.ui.UserPropertiesEditor;
import simtools.util.AbstractCyclicCallerRunnable;
/**
* Class Viewer
* This class permits to view, save, filter the java's JUL log system.
*/
public class Viewer extends JFrame {
/**(<b>Viewer</b>) instance: the singleton of the class.*/
protected static Viewer instance;
/**(<b>UserProperties</b>) userProperties: the properties.*/
protected UserProperties userProperties;
/**(<b>MenuResourceBundle</b>) resources: the resources.*/
protected MenuResourceBundle resources;
// Properties Management => save and restore size
static protected final String PROPERTY_WIDTH = "jlogtool.Viewer.width";
static protected final String PROPERTY_HEIGHT = "jlogtool.Viewer.height";
static protected final String PROPERTY_LOCATION_X = "jlogtool.Viewer.location.x";
static protected final String PROPERTY_LOCATION_Y = "jlogtool.Viewer.location.y";
static protected final String PROPERTY_FOLDER = "jlogtool.Viewer.folder";
// Properties Management => default TCP/IP port
static protected final String PROPERTY_PORT = "jlogtool.port";
/**(<b>LoggingBufferPanel</b>) panel: the main panel, that contains the JTable with the logs.*/
private LoggingBufferPanel panel;
/**(<b>HashMap</b>) actions: The hashMap that contains all the actions of the GUI.*/
private HashMap actions;
//The ACTIONS PROPERTIES
private static final String ACTION_OPEN_FILE = "openFile";
private static final String ACTION_SAVE_FILE = "saveFile";
private static final String ACTION_OPEN_SERVER = "openServer";
private static final String ACTION_CLOSE_SERVER = "closeServer";
private static final String ACTION_CLEAR_LOGS = "clearLogs";
private static final String ACTION_OPTIONS = "options";
private static final String ACTION_QUIT = "quit";
/**(<b>int</b>) DEFAULT_PORT: The default server port.*/
private static final int DEFAULT_PORT = 3009;
/**(<b>JLabel</b>) jLabelViewerInfo: the status label that will be used to display message to user.*/
private JLabel jLabelViewerInfo;
/**(<b>JLabel</b>) jLabelServerStatus: The jlabel that is used to display the server status.*/
private JLabel jLabelServerStatus;
/**
* Contructor Viewer
* <br><b>Summary:</b><br>
* The constructor of the class Viewer.
*/
public Viewer(){
//set the singleton.
instance = this;
init();
setTitle(getString("title"));
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
quitAction();
}
});
createContent();
initJMenuBar();
}
/**
* Method initJMenuBar
* <br><b>Summary:</b><br>
* This method initialise the JMenuBar.
* The actions need to have been intialized before calling this method.
*/
private void initJMenuBar() {
JMenuBar jMenuBar = new JMenuBar();
setJMenuBar(jMenuBar);
//The FILE menu
JMenu fileMenu = new JMenu(getString("fileMenuTitle"));
addMenuItem(fileMenu, ACTION_OPEN_FILE);
addMenuItem(fileMenu, ACTION_SAVE_FILE);
addMenuItem(fileMenu, ACTION_QUIT);
jMenuBar.add(fileMenu);
//The LOGS menu
JMenu logsMenu = new JMenu(getString("logsMenuTitle"));
addMenuItem(logsMenu, ACTION_CLEAR_LOGS);
jMenuBar.add(logsMenu);
//The Server menu
JMenu serverMenu = new JMenu(getString("serverMenuTitle"));
addMenuItem(serverMenu, ACTION_OPEN_SERVER);
addMenuItem(serverMenu, ACTION_CLOSE_SERVER);
jMenuBar.add(serverMenu);
//The options Menu
JMenu optionsMenu = new JMenu(getString("optionsMenuTitle"));
addMenuItem(optionsMenu, ACTION_OPTIONS);
jMenuBar.add(optionsMenu);
}
/**
* Method addMenuItem
* <br><b>Summary:</b><br>
* Add a menu item action to the menu, adding a mnemonic item if available in the resources.
* @param menu The menu to add the action.
* @param tag The tag of the action to add.
*/
private void addMenuItem(JMenu menu, String tag) {
Action action = (Action) actions.get(tag);
JMenuItem item = new JMenuItem(action);
String s = getString(tag + "MnemonicIndex");
if (s != null) {
int i = getInt(tag + "MnemonicIndex");
item.setDisplayedMnemonicIndex(i);
}
menu.add(item);
}
/**
* Method createContent
* <br><b>Summary:</b><br>
* This method create the object that compose the GUI.
*/
private void createContent(){
panel=new LoggingBufferPanel(this,"jlogtool",resources);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(panel, BorderLayout.CENTER);
getContentPane().add(getStatusPanel(), BorderLayout.SOUTH);
//Create the Cyclic caller that refresh the server status.
ServerIndicatorCyclicCaller cyclicRunnable = new ServerIndicatorCyclicCaller(panel.getBuffer(), jLabelServerStatus);
Thread cyclicThread = new Thread(cyclicRunnable, "ServerIndicatorThread");
cyclicThread.start();
}
/**
* Method getStatusPanel
* <br><b>Summary:</b><br>
* return the statusPanel
* @return <b>(JPanel)</b> A JPanel.
*/
private JPanel getStatusPanel(){
JPanel statusPanel = new JPanel(new GridBagLayout());
//add the status Label.
jLabelViewerInfo = new JLabel(getString("statusWelcome"));
GridBagConstraints c1 = new GridBagConstraints();
c1.gridx = 0;
c1.gridy = 0;
c1.anchor = GridBagConstraints.WEST;
c1.fill = GridBagConstraints.HORIZONTAL;
c1.weightx = 1;
statusPanel.add(jLabelViewerInfo, c1);
jLabelServerStatus = new JLabel("XXXXXXXX");
GridBagConstraints c2 = new GridBagConstraints();
c2.gridx = 1;
c2.gridy = 0;
c2.anchor = GridBagConstraints.EAST;
c2.fill = GridBagConstraints.HORIZONTAL;
c2.weightx = 1;
statusPanel.add(jLabelServerStatus);
return statusPanel;
}
/**
* Method init
* <br><b>Summary:</b><br>
* initialise stuff such as, userproperties, language and the actions.
*/
private void init(){
// Read properties => this contain the user preference, including language setup
userProperties=new UserProperties("jlogtool", true);
userProperties.read();
// language may be "fr", or "en_US", or whatever. If unsupported, it will default to best match
String language = userProperties.getString("jlogtool.language","");
// Let command-line -D JVM define take precedence
if ((System.getProperty("language")==null) || System.getProperty("language").equals(""))
if (!language.equals("")) {
int sep = language.indexOf('_');
if (sep==-1) {
CustomizedLocale.set(new Locale(language));
} else {
String lang = language.substring(0,sep);
String country = language.substring(sep+1);
sep = country.indexOf('_');
if (sep==-1) {
CustomizedLocale.set(new Locale(lang, country));
} else {
CustomizedLocale.set(new Locale(lang, country.substring(0,sep), country.substring(sep+1)));
}
}
}
// Now initialize resources => those use the Locale defined above.
resources = ResourceFinder.getMenu(Viewer.class);
//Initialize the actions.
initActions();
}
/**
* Method initActions
* <br><b>Summary:</b><br>
* This method initialize the available actions in Viewer.
*/
private void initActions() {
actions = new HashMap();
actions.put(ACTION_OPEN_FILE, new OpenFileViewerAction());
actions.put(ACTION_SAVE_FILE, new SaveFileViewerAction());
actions.put(ACTION_OPEN_SERVER, new OpenServerViewerAction());
actions.put(ACTION_CLOSE_SERVER, new CloseServerViewerAction());
actions.put(ACTION_CLEAR_LOGS, new ClearLogsViewerAction());
actions.put(ACTION_OPTIONS, new OptionsViewerAction());
actions.put(ACTION_QUIT, new QuitViewerAction());
}
/**
* Method saveProperties
* <br><b>Summary:</b><br>
* Save the Viewer window property to userProperties.
*/
private void saveProperties() {
//save windows properties.
userProperties.setInt(PROPERTY_WIDTH, getWidth());
userProperties.setInt(PROPERTY_HEIGHT, getHeight());
userProperties.setInt(PROPERTY_LOCATION_X, (int) getLocation().getX());
userProperties.setInt(PROPERTY_LOCATION_Y, (int) getLocation().getY());
//do not forget the
panel.getUserProperties(userProperties);
}
/**
* Method loadProperties
* <br><b>Summary:</b><br>
* Use this method to load the properties, and apply them to current GUI.
* @return <b>(void)</b> A void.
*/
private void loadProperties() {
//load windows properties.
setSize(userProperties.getInt(PROPERTY_WIDTH, 700),
userProperties.getInt(PROPERTY_HEIGHT, 500));
setLocation(new Point(userProperties.getInt(PROPERTY_LOCATION_X, 0), userProperties.getInt(PROPERTY_LOCATION_Y, 0)));
panel.setUserProperties(userProperties);
}
/**
* Method open
* <br><b>Summary:</b><br>
* The method to open a log file under binary or xml format.
* @param file The log file to be opened under binary or xml format.
*/
private void open(File file) {
String fileName = file.getName();
try {
//clear the logs before opening a log file.
clearLogsAction();
//indicate in status.
setViewerInfo(getString("statusOpeningFile")+": "+fileName);
//And if server is open, close it.
if(panel.getBuffer().isSocketServerOpen() != -1){
closeServerAction();
}
//check on file extension, to know if we have to read a binary or a xml log file.
if (fileName.endsWith(".log")) {
//It is a xml log file.
panel.getBuffer().readXML(file);
} else if (fileName.endsWith(".logb")) {
//It is a binary log file.
panel.getBuffer().read(file);
}else{
//we can not read this type of message.
setViewerInfo("statusNotRecognizedFileType"+fileName);
}
//indicate the table that data have changed.
panel.getLogbookModel().fireTableDataChanged();
} catch (ParserConfigurationException e) {
e.printStackTrace();
setViewerInfo(getString("statusFailedToOpenFile")+" "+fileName+": "+e.getMessage());
} catch (SAXException e) {
e.printStackTrace();
setViewerInfo(getString("statusFailedToOpenFile")+" "+fileName+": "+e.getMessage());
} catch (IOException e) {
e.printStackTrace();
setViewerInfo(getString("statusFailedToOpenFile")+" "+fileName+": "+e.getMessage());
}
}
/**
* Method openServer
* <br><b>Summary:</b><br>
* Use this method to open a listening log server on the given port.
* @param port The port to listen for connection.
*/
private void openServer(int port){
try {
panel.getBuffer().openServer(port);
//Change status.
setViewerInfo(getString("statusServerOpenOnPort")+" "+port);
} catch (IOException e) {
setViewerInfo(getString("statusCantOpenServerBecause")+ " "+e.getMessage());
e.printStackTrace();
}
((LoggingBufferTableModel)panel.getLogbookModel()).startRefreshLoop();
}
/**
* Method main
* <br><b>Summary:</b><br>
* @param args A log file to open under binary format (*.logb) or xml format (*.xml) or a port number to listen to.
*/
public static void main(String[] args){
Viewer v=new Viewer();
v.pack();
v.loadProperties();
v.show();
//Parse arguments.
if(args.length>0){
//If first arg is a file that exist, it should be a log file to be opened.
File f=new File(args[0]);
if(f.exists()){
v.open(f);
}
else{
//else it should be a port to listen to.
try{
int port=Integer.parseInt(args[0]);
v.openServer(port);
}catch(NumberFormatException e){
}
}
}
else{
//If no arguments is precised, open a server on default port.
v.openServer(DEFAULT_PORT);
}
}
/**
* Class OpenFileViewerAction.
* The action to open a file.
*/
private class OpenFileViewerAction extends AbstractViewerAction{
public OpenFileViewerAction(){
super(ACTION_OPEN_FILE);
}
public void actionPerformed(ActionEvent e) {
openAction();
}
}
/**
* Method openAction
* <br><b>Summary:</b><br>
* Use this method to open a file and view its content in the log viewer.
* Can open wether a XML, or a binary file.
*/
private void openAction() {
JDialogFile dialogFile = new JDialogFile(JDialogFile.TYPE_OPEN);
dialogFile.show();
//If used had validated the dialog, retrieve the selected file to open.
if(dialogFile.hasBeenValidated()){
//retrieve the file to open
File fileToOpen = dialogFile.getResult();
//open it
open(fileToOpen);
//save the file just open in the properties, to open files easily.
userProperties.setProperty(PROPERTY_FOLDER, fileToOpen.getPath());
}
}
/**
* Class SaveFileViewerAction.
* The action to save a file.
*/
private class SaveFileViewerAction extends AbstractViewerAction{
public SaveFileViewerAction(){
super(ACTION_SAVE_FILE);
}
public void actionPerformed(ActionEvent e) {
saveAction();
}
}
/**
* Method saveAction
* <br><b>Summary:</b><br>
* Use this method to save to a file.
*/
private void saveAction() {
JDialogFile dialogFile = new JDialogFile(JDialogFile.TYPE_SAVE);
dialogFile.show();
//If used had validated the dialog, retrieve the selected file to open.
if(dialogFile.hasBeenValidated()){
File fileTosave = dialogFile.getResult();
//If file does not end with ".logb" add suffix.
if(!fileTosave.getName().endsWith(".logb")){
fileTosave = new File(fileTosave.getAbsoluteFile()+".logb");
}
try {
panel.getBuffer().save(fileTosave);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* Class OpenServerViewerAction.
* The action to open a server to listen to a socket.
*/
private class OpenServerViewerAction extends AbstractViewerAction{
public OpenServerViewerAction(){
super(ACTION_OPEN_SERVER);
}
public void actionPerformed(ActionEvent e) {
openServerAction();
}
}
/**
* Method openServerAction
* <br><b>Summary:</b><br>
* Use this method to open a server to listen to a socket.
*/
private void openServerAction() {
//Open a dialog to enter port number.
int port=userProperties.getInt(PROPERTY_PORT, DEFAULT_PORT);
String newPort = JOptionPane.showInputDialog(getString("openServerPortMessage"), ""+port);
try{
port = Integer.parseInt(newPort);
}catch(NumberFormatException e){
setViewerInfo(getString("statusBadPortNumberUsing")+ " "+port);
port = DEFAULT_PORT;
}
openServer(port);
}
/**
* Class CloseServerViewerAction.
* The action to Close a server to listen to a socket.
*/
private class CloseServerViewerAction extends AbstractViewerAction{
public CloseServerViewerAction(){
super(ACTION_CLOSE_SERVER);
}
public void actionPerformed(ActionEvent e) {
closeServerAction();
}
}
/**
* Method closeServerAction
* <br><b>Summary:</b><br>
* Use this method to Close a server.
*/
private void closeServerAction() {
try {
panel.getBuffer().closeServer();
((LoggingBufferTableModel)panel.getLogbookModel()).stopRefreshLoop();
//Change status.
setViewerInfo(getString("statusServerClose"));
} catch (IOException e) {
setViewerInfo(getString("statusCantCloseServerBecause")+ " "+e.getMessage());
e.printStackTrace();
}
}
/**
* Class ClearLogsViewerAction.
* The action to clear the logs.
*/
private class ClearLogsViewerAction extends AbstractViewerAction{
public ClearLogsViewerAction(){
super(ACTION_CLEAR_LOGS);
}
public void actionPerformed(ActionEvent e) {
clearLogsAction();
}
}
/**
* Method clearLogsAction
* <br><b>Summary:</b><br>
* Use this method to clear the logs.
*/
private void clearLogsAction() {
panel.clearLogs();
setViewerInfo(getString("statusLogsCleared"));
}
/**
* Class OptionsViewerAction.
* The action to display the options dialog.
*/
private class OptionsViewerAction extends AbstractViewerAction{
public OptionsViewerAction(){
super(ACTION_OPTIONS);
}
public void actionPerformed(ActionEvent e) {
optionsAction();
}
}
/**
* Method optionsAction
* <br><b>Summary:</b><br>
* Use this method to display the options dialog.
*/
private void optionsAction() {
//Construct the properties editor that will be used to edit/import/export the properties.
UserPropertiesEditor propEditor=new UserPropertiesEditor(instance);
propEditor.setLocationRelativeTo(instance);
//show the editor.
if(propEditor.showAndUpdate(userProperties,true)){
//If we are here, it means that users changes the properties, and save the changes.
setViewerInfo(getString("statusOptionsChanged"));
}
}
/**
* Class QuitViewerAction.
* The action to quit the viewer.
*/
private class QuitViewerAction extends AbstractViewerAction{
public QuitViewerAction(){
super(ACTION_QUIT);
}
public void actionPerformed(ActionEvent e) {
quitAction();
}
}
/**
* Method quitAction
* <br><b>Summary:</b><br>
* Use this method to quit the viewer.
*/
private void quitAction() {
saveProperties();
userProperties.write();
System.exit(0);
}
/**
* Method getString
* <br><b>Summary:</b><br>
* Return the resource that correspond to the given key.
* @param key The key to search.
* @return <b>(String)</b> A String: the resource that correspond to the given key..
*/
protected String getString(String key){
return resources.getString(key);
}
/**
* Method getInt
* <br><b>Summary:</b><br>
* Return the resource that correspond to the given key.
* @param key The key to search.
* @return <b>(int)</b> A int: the resource that correspond to the given key..
*/
protected int getInt(String key){
return resources.getIntValue(key);
}
/**
* Method setViewerInfo
* <br><b>Summary:</b><br>
* Use this method to diaply a status message in the status bar.
* @param status The status message to display.
*/
private void setViewerInfo(String status){
jLabelViewerInfo.setText(status);
}
/**
* Class ServerIndicatorRunnable
* A cyclic caller, to retrieve the status of the server, and set it in the server status component.
*/
private class ServerIndicatorCyclicCaller extends AbstractCyclicCallerRunnable{
/**(<b>LoggingEntryByteBuffer</b>) buffer: The buffer that permits to get the status of the server.*/
private LoggingEntryByteBuffer buffer;
/**(<b>JLabel</b>) jLabelServerIndicator: The JLabel used to indicate server status.*/
private JLabel jLabelServerIndicator;
/**(<b>String[]</b>) OPEN_ANIMATION: The animation to use to indicate that server is opened.*/
private final String[] OPEN_ANIMATION = new String[]{"----<#", "---<#-", "--<#--", "-<#---", "<#----", ">-----", "-->---", "--->--", "---->-", "----->"};
/**(<b>int</b>) openIndex:the current index in the open animation.*/
private int openIndex;
/**
* Contructor ServerIndicatorCyclicCaller
* <br><b>Summary:</b><br>
* The constructor of the class ServerIndicatorCyclicCaller.
* @param buffer The buffer to use to retrieve the server status.
*/
public ServerIndicatorCyclicCaller(LoggingEntryByteBuffer buffer, JLabel jLabelServerIndicator){
this.buffer = buffer;
this.jLabelServerIndicator = jLabelServerIndicator;
openIndex = 0;
}
/* (non-Javadoc)
* @see simtools.util.AbstractCyclicCallerRunnable#process()
*/
public void process() {
int port = buffer.isSocketServerOpen();
//If a server is opened, use open animation.
if(port != -1){
jLabelServerIndicator.setText(OPEN_ANIMATION[openIndex]+port+OPEN_ANIMATION[openIndex]);
openIndex++;
if(openIndex == OPEN_ANIMATION.length){
openIndex = 0;
}
}else{
jLabelServerIndicator.setText("XXXX");
}
}
}
}