package org.oddjob.monitor;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JSeparator;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import org.apache.log4j.Logger;
import org.oddjob.FailedToStopException;
import org.oddjob.Oddjob;
import org.oddjob.OddjobServices;
import org.oddjob.OddjobShutdownThread;
import org.oddjob.Stoppable;
import org.oddjob.arooa.ArooaConfiguration;
import org.oddjob.arooa.ArooaParseException;
import org.oddjob.arooa.ArooaSession;
import org.oddjob.arooa.ConfigurationHandle;
import org.oddjob.arooa.deploy.annotations.ArooaAttribute;
import org.oddjob.arooa.design.view.ScreenPresence;
import org.oddjob.arooa.design.view.Standards;
import org.oddjob.arooa.parsing.ArooaContext;
import org.oddjob.arooa.parsing.ConfigOwnerEvent;
import org.oddjob.arooa.parsing.ConfigSessionEvent;
import org.oddjob.arooa.parsing.ConfigurationOwner;
import org.oddjob.arooa.parsing.ConfigurationSession;
import org.oddjob.arooa.parsing.ElementConfiguration;
import org.oddjob.arooa.parsing.OwnerStateListener;
import org.oddjob.arooa.parsing.SessionStateListener;
import org.oddjob.arooa.standard.StandardArooaSession;
import org.oddjob.arooa.xml.XMLArooaParser;
import org.oddjob.framework.SerializableJob;
import org.oddjob.monitor.context.AncestorSearch;
import org.oddjob.monitor.control.PropertyPolling;
import org.oddjob.monitor.model.ConfigContextInialiser;
import org.oddjob.monitor.model.ExplorerModel;
import org.oddjob.monitor.model.ExplorerModelImpl;
import org.oddjob.monitor.model.FileHistory;
import org.oddjob.monitor.model.JobTreeNode;
import org.oddjob.monitor.view.ExplorerComponent;
import org.oddjob.monitor.view.MonitorMenuBar;
import org.oddjob.state.State;
import org.oddjob.swing.SwingInputHandler;
import org.oddjob.util.SimpleThreadManager;
import org.oddjob.util.ThreadManager;
/**
* @oddjob.description Runs Oddjob Explorer.
* <p>
* In the log panel the log level shown is set to be that of the
* rootLogger in the log4j.properties file in the <code>opt/classes</code>
* directory. By default it is set to INFO so you will not see
* DEBUG messages in the log panel. For more information on configuring the
* file see <a hre="http://logging.apache.org/log4j">
* http://logging.apache.org/log4j</a>
*
* @author Rob Gordon
*/
public class OddjobExplorer extends SerializableJob
implements Stoppable {
private static final long serialVersionUID = 2011101400L;
private static final Logger logger = Logger.getLogger(OddjobExplorer.class);
public static final String ODDJOB_PROPERTY ="oddjob";
public static final String DEFAULT_TITLE = "Oddjob Explorer";
protected transient VetoableChangeSupport vetoableChangeSupport;
/**
* @oddjob.property
* @oddjob.description The directory the file chooser
* should use when opening and saving Oddjobs.
* @oddjob.required No.
*/
private File dir;
/**
* @oddjob.property
* @oddjob.description The root node of jobs to monitor.
* @oddjob.required No.
*/
private transient volatile Oddjob oddjob;
private transient ConfigurationSession focus;
/**
* @oddjob.property
* @oddjob.description How often to poll in milli seconds for property updates.
* @oddjob.required No.
*/
private long pollingInterval = 5000;
/**
* @oddjob.property
* @oddjob.description The log format for formatting log messages. For more
* information on the format please see <a href="http://logging.apache.org/log4j/docs/">
* http://logging.apache.org/log4j/docs/</a>
* @oddjob.required No.
*/
private transient String logFormat;
/**
* @oddjob.property
* @oddjob.description A file to show when the explorer starts.
* @oddjob.required No.
*/
private File file;
/** The frame */
private volatile transient JFrame frame;
private transient Action newExplorerAction;
private transient Action newAction;
private transient Action openAction;
private transient Action saveAction;
private transient Action saveAsAction;
private transient Action closeAction;
private transient Action exitAction;
private transient JMenu fileMenu;
private transient ExplorerModel explorerModel;
private transient ExplorerComponent explorerComponent;
private transient MonitorMenuBar menuBar;
/** Poll for property changes. */
private transient PropertyPolling propertyPolling;
/**
* The ThreadManager. This job will not
* stop until the ThreadManager says it can.
*/
private transient ThreadManager threadManager;
/**
* @oddjob.property
* @oddjob.description Internal services. Set automatically
* by Oddjob.
* @oddjob.required No.
*/
private transient OddjobServices oddjobServices;
private FileHistory fileHistory;
// These will be serialzed so frame settings are preserved.
private ScreenPresence screen;
/** Used to track modification changes. */
transient private Set<ConfigurationOwner> owners;
/**
* Constructor to be used to create a single instance of this explorer.
* Typically used from code to aid debugging an Oddjob.
*/
public OddjobExplorer() {
fileHistory = new FileHistory();
ScreenPresence whole = ScreenPresence.wholeScreen();
screen = whole.smaller(0.66);
completeConstruction();
}
/**
* Constructor when this explorer is being created as one of many.
*
* @param controller
* @param screen
* @param sharedFileHistory
*/
public OddjobExplorer(MultiViewController controller,
ScreenPresence screen,
FileHistory sharedFileHistory) {
this.screen = screen;
this.fileHistory = sharedFileHistory;
this.newExplorerAction = new NewExplorerAction(controller);
completeConstruction();
}
/**
* Completes construction of this object. Required because de-serialisation
* doesn't go through the constructor.
*/
private void completeConstruction() {
vetoableChangeSupport =
new VetoableChangeSupport(this);
owners = new LinkedHashSet<ConfigurationOwner>();
newAction = new NewAction();
openAction = new OpenAction();
saveAction = new SaveAction();
saveAsAction = new SaveAsAction();
closeAction = new CloseAction();
exitAction = new ExitAction();
fileHistory.addChangeAction(new Runnable() {
@Override
public void run() {
if (frame == null) {
fileHistory.removeChangeAction(this);
}
else {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
updateFileMenu();
}
});
}
}
});
}
protected ExplorerComponent getExplorerComponent() {
return explorerComponent;
}
/**
* Capture services from the containing Oddjob.
*
* @param oddjobServices
*/
@Inject
public void setOddjobServices(OddjobServices oddjobServices) {
this.oddjobServices = oddjobServices;
}
/*
* (non-Javadoc)
* @see org.oddjob.framework.BaseComponent#setArooaSession(org.oddjob.arooa.ArooaSession)
*/
@Override
public void setArooaSession(ArooaSession session) {
super.setArooaSession(session);
propertyPolling = new PropertyPolling(this, session);
}
/**
* Setter for Oddjob. This will change the Oddjob being monitored.
*
* @param oddjob The new Oddjob or null to just close.
*
* @throws PropertyVetoException If the exsiting Oddjob can't be
* closed.
*/
public void setOddjob(Oddjob oddjob) throws PropertyVetoException {
if (this.oddjob == oddjob) {
return;
}
Object oldValue = this.oddjob;
vetoableChangeSupport.fireVetoableChange(ODDJOB_PROPERTY, oldValue, oddjob);
if (this.oddjob != null) {
addFileHistory(this.oddjob.getFile());
this.oddjob.destroy();
}
this.oddjob = oddjob;
if (oddjob != null && oddjob.getDir() != null) {
this.dir = oddjob.getDir();
}
firePropertyChange(ODDJOB_PROPERTY, oldValue, this.oddjob);
}
public Oddjob getOddjob() {
return oddjob;
}
@ArooaAttribute
public void setDir(File dir) {
this.dir = dir;
}
public File getDir() {
return dir;
}
/**
* @oddjob.property title
* @oddjob.description The Explorer frame's title.
* @return The title.
*/
public String getTitle() {
return frame.getTitle();
}
/**
* Add to the file history that will be used in the file menu.
*
* @param file The file. If null then ignored.
*/
void addFileHistory(File file) {
if (file == null) {
return;
}
fileHistory.addHistory(file);
}
void updateFileMenu() {
fileMenu.removeAll();
if (newExplorerAction != null) {
fileMenu.add(new JMenuItem(newExplorerAction));
}
fileMenu.add(new JMenuItem(newAction));
fileMenu.add(new JMenuItem(openAction));
fileMenu.add(new JMenuItem(saveAction));
fileMenu.add(new JMenuItem(saveAsAction));
fileMenu.add(new JMenuItem(closeAction));
fileMenu.add(new JSeparator());
Action a[] = new Action[fileHistory.size()];
// reverse for recent first
for (int i = 0; i < fileHistory.size(); ++i) {
a[fileHistory.size() - i - 1] = new HistoryAction(fileHistory.size() - i, (File) fileHistory.get(i));
}
boolean hasHistory = false;
for (int i = 0; i < a.length; ++i) {
hasHistory = true;
fileMenu.add(new JMenuItem(a[i]));
}
if (hasHistory) {
fileMenu.add(new JSeparator());
}
fileMenu.add(new JMenuItem(exitAction));
}
/**
*/
public void show() {
if (frame == null) {
throw new IllegalStateException("No frame - explorer must have stopped.");
}
frame.toFront();
}
class CheckOddjobStopped implements VetoableChangeListener {
public void vetoableChange(PropertyChangeEvent evt)
throws PropertyVetoException {
if (!ODDJOB_PROPERTY.equals(evt.getPropertyName())) {
return;
}
Oddjob oddjob = (Oddjob) evt.getOldValue();
if (oddjob == null) {
return;
}
State state = oddjob.lastStateEvent().getState();
if (state.isStoppable()) {
String message = "Oddjob is not stopped. Current state is " + state;
JOptionPane.showMessageDialog(frame, message, "Oddjob Running!", JOptionPane.ERROR_MESSAGE);
throw new PropertyVetoException(message, evt);
}
String[] active = threadManager.activeDescriptions();
if (active.length > 0) {
StringBuilder message = new StringBuilder();
message.append("The following are still running:\n\n");
for (int i = 0; i < active.length; ++i) {
message.append(active[i]);
message.append('\n');
}
message.append('\n');
JOptionPane.showMessageDialog(frame, message, "Jobs Running!", JOptionPane.ERROR_MESSAGE);
throw new PropertyVetoException(message.toString(), evt);
}
}
}
class TrackConfigurationOwners
implements TreeModelListener {
public void treeNodesChanged(TreeModelEvent e) {
}
public void treeStructureChanged(TreeModelEvent e) {
}
public void treeNodesInserted(TreeModelEvent event) {
for (Object child: event.getChildren()) {
Object component = ((JobTreeNode) child).getComponent();
if (component instanceof ConfigurationOwner) {
owners.add((ConfigurationOwner) component);
}
}
}
public void treeNodesRemoved(TreeModelEvent event) {
for (Object child : event.getChildren()) {
Object component = ((JobTreeNode) child).getComponent();
owners.remove(component);
}
}
}
/** Don't want to notify of changes during save as action. */
private boolean saveAs = false;
/**
* Track ConfigurationOwners. Used to check we can stop.
*/
class CheckConfigurationsSaved
implements
VetoableChangeListener {
public void vetoableChange(PropertyChangeEvent evt)
throws PropertyVetoException {
if (!ODDJOB_PROPERTY.equals(evt.getPropertyName())) {
return;
}
Oddjob oldOddjob = (Oddjob) evt.getOldValue();
if (oldOddjob != null) {
List<ConfigurationOwner> modified = new ArrayList<ConfigurationOwner>();
for (ConfigurationOwner owner : owners) {
if (saveAs && owner == oldOddjob) {
continue;
}
ConfigurationSession session = owner.provideConfigurationSession();
if (session != null && session.isModified()) {
modified.add(owner);
}
}
if (!modified.isEmpty() && !canClose(modified)) {
throw new PropertyVetoException("Outstanding Modifications", evt);
}
}
owners.clear();
Oddjob newOddjob = (Oddjob) evt.getNewValue();
if (newOddjob != null) {
owners.add(newOddjob);
}
}
boolean canClose(Collection<ConfigurationOwner> modified) {
StringBuilder message = new StringBuilder();
message.append("Unsaved modifications! Continue?\n\n");
for (ConfigurationOwner owner : modified) {
message.append(owner.toString());
message.append('\n');
}
message.append('\n');
int option = JOptionPane.showConfirmDialog(
explorerComponent,
message.toString(),
"Unsaved Modifications",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.WARNING_MESSAGE);
if (option == JOptionPane.OK_OPTION) {
return true;
}
else {
return false;
}
}
}
// This is a bit of a bodge because the ChangeFocus class is created
// twice so can't contain state.
private transient String name;
private transient boolean modified;
private transient ConfigurationOwner current;
/**
* Track current configuration. Update the title.
*/
class ChangeFocus
implements
PropertyChangeListener,
TreeSelectionListener,
OwnerStateListener,
SessionStateListener {
public void propertyChange(PropertyChangeEvent evt) {
if (!ODDJOB_PROPERTY.equals(evt.getPropertyName())) {
return;
}
Oddjob newJob = (Oddjob) evt.getNewValue();
setOwner(newJob);
}
public void valueChanged(TreeSelectionEvent e) {
// tried: (JobTreeNode) e.getPath().getLastPathComponent();
// but it's not null id de-selection.
JobTreeNode selected =
(JobTreeNode) ((JTree) e.getSource()).getLastSelectedPathComponent();
if (selected == null) {
setOwner(oddjob);
}
else {
AncestorSearch search = new AncestorSearch(
selected.getExplorerContext());
ConfigurationOwner configOwner =
(ConfigurationOwner) search.getValue(
ConfigContextInialiser.CONFIG_OWNER);
setOwner(configOwner);
}
}
public void sessionChanged(ConfigOwnerEvent event) {
updateSession(event.getSource().provideConfigurationSession());
writeTitle();
}
public void sessionModifed(ConfigSessionEvent event) {
modified = true;
writeTitle();
}
public void sessionSaved(ConfigSessionEvent event) {
modified = false;
writeTitle();
}
void setOwner(ConfigurationOwner owner) {
if (current == owner) {
return;
}
if (current != null) {
current.removeOwnerStateListener(this);
}
if (owner == null) {
name = null;
modified = false;
updateSession(null);
}
else {
name = owner.toString();
updateSession(owner.provideConfigurationSession());
owner.addOwnerStateListener(this);
}
current = owner;
writeTitle();
}
void updateSession(ConfigurationSession session) {
if (focus != null) {
focus.removeSessionStateListener(this);
}
focus = session;
if (focus == null) {
modified = false;
}
else {
focus.addSessionStateListener(this);
modified = focus.isModified();
}
}
void writeTitle() {
if (frame == null) {
return;
}
String title = DEFAULT_TITLE;
if (name != null) {
title += " - " + name + (modified ? " *" : "");
}
frame.setTitle(title);
}
}
class ChangeView implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent evt) {
if (!ODDJOB_PROPERTY.equals(evt.getPropertyName())) {
return;
}
Oddjob oldJob = (Oddjob) evt.getOldValue();
Oddjob newJob = (Oddjob) evt.getNewValue();
if (oldJob != null) {
menuBar.noSession();
explorerComponent.destroy();
explorerModel.destroy();
frame.getContentPane().removeAll();
}
if (newJob != null) {
ExplorerModelImpl explorerModel = new ExplorerModelImpl(
new StandardArooaSession());
explorerModel.setThreadManager(threadManager);
explorerModel.setLogFormat(logFormat);
explorerModel.setOddjob(newJob);
OddjobExplorer.this.explorerModel = explorerModel;
explorerComponent = new ExplorerComponent(
explorerModel,
propertyPolling);
explorerComponent.bindTo(menuBar);
JTree tree = explorerComponent.getTree();
tree.addTreeSelectionListener(new ChangeFocus());
tree.getModel().addTreeModelListener(new TrackConfigurationOwners());
frame.getContentPane().add(explorerComponent);
// frame.pack();
explorerComponent.balance();
}
frame.validate();
frame.repaint();
}
}
/**
* Helper method to create the menu bar.
*
*/
void createView() {
menuBar = new MonitorMenuBar();
fileMenu = menuBar.getFileMenu();
updateFileMenu();
frame = new JFrame();
screen.fit(frame);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
// Because we DO_NOTHING_ON_CLOSE it's up to us to
// close the window.
maybeCloseWindow();
}
public void windowClosed(WindowEvent e) {
logger.debug("Explorer window closed.");
}
});
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
frame.setJMenuBar(menuBar);
frame.setTitle(DEFAULT_TITLE);
}
/*
* (non-Javadoc)
* @see org.oddjob.jobs.AbstractJob#execute()
*/
protected int execute() throws Exception {
threadManager = new SimpleThreadManager();
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
createView();
// In case Explorer was started with an Oddjob.
final Oddjob oddjob = this.oddjob;
this.oddjob = null;
VetoableChangeListener checkStop = new CheckOddjobStopped();
VetoableChangeListener checkSaved = new CheckConfigurationsSaved();
// Order important otherwise we see modified indicator on destroy.
PropertyChangeListener changeTitle = new ChangeFocus();
PropertyChangeListener changeView = new ChangeView();
vetoableChangeSupport.addVetoableChangeListener(checkStop);
vetoableChangeSupport.addVetoableChangeListener(checkSaved);
addPropertyChangeListener(changeView);
addPropertyChangeListener(changeTitle);
frame.setVisible(true);
frame.addComponentListener(new ComponentAdapter() {
@Override
public void componentMoved(ComponentEvent e) {
screen = new ScreenPresence(e.getComponent());
}
@Override
public void componentResized(ComponentEvent e) {
screen = new ScreenPresence(e.getComponent());
}
});
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
if(oddjob != null) {
setOddjob(oddjob);
} else if(getFile() != null) {
open(getFile());
}
} catch (PropertyVetoException e) {
// Ignore
}
}
});
while (!stop) {
try {
if (propertyPolling == null) {
logger().info("No property polling. set ArooaSession to enable polling.");
}
else {
propertyPolling.poll();
}
}
catch (RuntimeException e) {
logger().error("Property polling failed.", e);
}
synchronized (this) {
try {
wait(pollingInterval);
} catch (InterruptedException e) {
break;
}
}
}
vetoableChangeSupport.removeVetoableChangeListener(checkStop);
vetoableChangeSupport.removeVetoableChangeListener(checkSaved);
removePropertyChangeListener(changeView);
removePropertyChangeListener(changeTitle);
threadManager.close();
return 0;
}
/**
* Maybe close the window if it isn't vetoed be attempting to remove
* Oddjob.
*/
private void maybeCloseWindow() {
try {
setOddjob(null);
} catch (PropertyVetoException e) {
logger.info("Can't close: " + e.getMessage());
return;
}
closeWindow();
}
/**
* Close window regardless. No veto here.
*/
private void closeWindow() {
stop = true;
// Wake up the log poller so it sees that we've stopped.
synchronized (OddjobExplorer.this) {
OddjobExplorer.this.notifyAll();
}
final JFrame frame = this.frame;
if (frame != null) {
// This hangs from the shutdown hook. I don't know
// why. Invoking on the Event Dispatch thread makes no difference.
if (!(Thread.currentThread() instanceof OddjobShutdownThread)) {
frame.dispose();
}
this.frame = null;
}
logger().debug("Monitor closed.");
}
public void onStop() throws FailedToStopException {
// Note that we close window first otherwise changing
// window title deadlocks in the shutdown hook.
closeWindow();
Oddjob oddjob = this.oddjob;
if (oddjob != null) {
oddjob.stop();
try {
setOddjob(null);
} catch (PropertyVetoException e) {
// stop should mean this doesn't happen - but just in case:
this.oddjob = null;
}
}
}
/**
* Helper method to create a new Oddjob.
*
* @return A new Oddjob.
*/
private Oddjob newOddjob() {
Oddjob oddjob = new Oddjob();
oddjob.setArooaSession(getArooaSession());
oddjob.setOddjobServices(oddjobServices);
oddjob.setInputHandler(new SwingInputHandler(frame));
return oddjob;
}
class NewExplorerAction extends AbstractAction {
private static final long serialVersionUID = 2011090600;
private final MultiViewController multiViewController;
NewExplorerAction(MultiViewController multiViewController) {
this.multiViewController = multiViewController;
putValue(Action.NAME, "New Explorer");
putValue(Action.MNEMONIC_KEY, Standards.NEW_EXPLORER_MNEMONIC_KEY);
putValue(Action.ACCELERATOR_KEY, Standards.NEW_EXPLORER_ACCELERATOR_KEY);
}
@Override
public void actionPerformed(ActionEvent e) {
multiViewController.launchNewExplorer(OddjobExplorer.this);
}
}
class NewAction extends AbstractAction {
private static final long serialVersionUID = 2008120400;
NewAction() {
putValue(Action.NAME, "New Oddjob");
putValue(Action.MNEMONIC_KEY, Standards.NEW_MNEMONIC_KEY);
putValue(Action.ACCELERATOR_KEY, Standards.NEW_ACCELERATOR_KEY);
}
/*
* (non-Javadoc)
*
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(final ActionEvent e) {
try {
Oddjob newJob = newOddjob();
ArooaConfiguration saveAsConfig = new ArooaConfiguration() {
public ConfigurationHandle parse(ArooaContext parentContext)
throws ArooaParseException {
final ConfigurationHandle handle = new ElementConfiguration(
Oddjob.ODDJOB_ELEMENT).parse(parentContext);
return new ConfigurationHandle() {
public ArooaContext getDocumentContext() {
return handle.getDocumentContext();
};
public void save() throws ArooaParseException {
saveAsAction.actionPerformed(e);
};
};
}
};
newJob.setConfiguration(saveAsConfig);
newJob.load();
setOddjob(newJob);
}
catch (PropertyVetoException e1) {
// Ignore;
}
catch (RuntimeException ex) {
logger().warn("Exception creating new Oddjob.", ex);
JOptionPane.showMessageDialog(frame, ex.getMessage(), "Exception!", JOptionPane.ERROR_MESSAGE);
}
}
}
class SaveAction extends AbstractAction {
private static final long serialVersionUID = 2008111300;
SaveAction() {
putValue(Action.NAME, "Save");
putValue(Action.MNEMONIC_KEY, Standards.SAVE_MNEMONIC_KEY);
putValue(Action.ACCELERATOR_KEY, Standards.SAVE_ACCELERATOR_KEY);
OddjobExplorer.this.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
setEnabled(oddjob != null);
}
});
}
/*
* (non-Javadoc)
*
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(ActionEvent e) {
if (focus == null) {
return;
}
try {
focus.save();
}
catch (Exception exception) {
logger.error("Failed creating Design from XML.", exception);
JOptionPane.showMessageDialog(
frame.getContentPane(),
exception.getMessage(),
"Error",
JOptionPane.ERROR_MESSAGE);
return;
}
}
}
class SaveAsAction extends AbstractAction {
private static final long serialVersionUID = 2008111300;
SaveAsAction() {
putValue(Action.NAME, "Save As...");
putValue(Action.MNEMONIC_KEY, Standards.SAVEAS_MNEMONIC_KEY);
putValue(Action.ACCELERATOR_KEY, Standards.SAVEAS_ACCELERATOR_KEY);
OddjobExplorer.this.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
setEnabled(oddjob != null);
}
});
}
/*
* (non-Javadoc)
*
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(ActionEvent e) {
ConfigurationSession config = oddjob.provideConfigurationSession();
if (config == null) {
JOptionPane.showMessageDialog(
frame.getContentPane(),
"No Configuration loaded - Run Oddjob first.",
"No Config",
JOptionPane.INFORMATION_MESSAGE);
return;
}
JFileChooser chooser = new JFileChooser();
if (dir != null) {
chooser.setCurrentDirectory(dir);
}
int option = chooser.showSaveDialog(frame);
if (option != JFileChooser.APPROVE_OPTION) {
return;
}
File file = chooser.getSelectedFile();
try {
XMLArooaParser parser = new XMLArooaParser();
ArooaConfiguration oddjobConfiguration =
config.dragPointFor(oddjob);
parser.parse(
oddjobConfiguration);
PrintWriter printWriter = new PrintWriter(new FileWriter(file));
printWriter.print(parser.getXml());
printWriter.close();
addFileHistory(oddjob.getFile());
Oddjob newJob = newOddjob();
newJob.setFile(file);
newJob.load();
saveAs = true;
setOddjob(newJob);
}
catch (PropertyVetoException e1) {
// Ignore;
}
catch (Exception exception) {
logger.error("Failed creating Design from XML.", exception);
JOptionPane.showMessageDialog(
frame.getContentPane(),
exception.getMessage(),
"Error",
JOptionPane.ERROR_MESSAGE);
return;
}
finally {
saveAs = false;
}
}
}
class OpenAction extends AbstractAction {
private static final long serialVersionUID = 2008120400;
OpenAction() {
putValue(Action.NAME, "Open");
putValue(Action.MNEMONIC_KEY, Standards.OPEN_MNEMONIC_KEY);
putValue(Action.ACCELERATOR_KEY, Standards.OPEN_ACCELERATOR_KEY);
}
/*
* (non-Javadoc)
*
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(ActionEvent e) {
JFileChooser chooser = new JFileChooser();
if (dir != null) {
chooser.setCurrentDirectory(dir);
}
int option = chooser.showOpenDialog(frame);
if (option != JFileChooser.APPROVE_OPTION) {
return;
}
open(chooser.getSelectedFile());
}
}
class CloseAction extends AbstractAction {
private static final long serialVersionUID = 2008120400;
CloseAction() {
putValue(Action.NAME, "Close");
putValue(Action.MNEMONIC_KEY, Standards.CLOSE_MNEMONIC_KEY);
putValue(Action.ACCELERATOR_KEY, Standards.CLOSE_ACCELERATOR_KEY);
OddjobExplorer.this.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
setEnabled(oddjob != null);
}
});
}
/*
* (non-Javadoc)
*
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(ActionEvent e) {
try {
setOddjob(null);
} catch (PropertyVetoException e1) {
// Ignore;
}
catch (RuntimeException ex) {
logger().warn("Exception closing Oddjob.", ex);
JOptionPane.showMessageDialog(frame, ex.getMessage(), "Exception!", JOptionPane.ERROR_MESSAGE);
}
}
}
class ExitAction extends AbstractAction {
private static final long serialVersionUID = 2008120400;
ExitAction() {
putValue(Action.NAME, "Exit");
putValue(Action.MNEMONIC_KEY, Standards.EXIT_MNEMONIC_KEY);
}
/*
* (non-Javadoc)
*
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(ActionEvent e) {
maybeCloseWindow();
}
}
class HistoryAction extends AbstractAction {
private static final long serialVersionUID = 2008120400;
private final File file;
HistoryAction(int number, File file) {
putValue(Action.NAME, "" + number + " " + file.getName() + " ["
+ file.getAbsoluteFile().getParent() + "]");
putValue(Action.MNEMONIC_KEY, new Integer(48 + number));
this.file = file;
}
/*
* (non-Javadoc)
*
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(ActionEvent e) {
try {
Oddjob newJob = newOddjob();
newJob.setFile(file);
newJob.load();
setOddjob(newJob);
}
catch (PropertyVetoException e1) {
// Ignore;
}
catch (RuntimeException ex) {
logger().warn("Exception opening file [" + file + "]", ex);
JOptionPane.showMessageDialog(frame, ex.getMessage(), "Exception!", JOptionPane.ERROR_MESSAGE);
}
}
}
/**
* Custom serialsation.
*/
private void writeObject(ObjectOutputStream s)
throws IOException {
s.defaultWriteObject();
}
/**
* Custom serialisation.
*/
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
completeConstruction();
}
/**
* @return Returns the pollingInterval.
*/
public synchronized long getPollingInterval() {
return pollingInterval;
}
/**
* @param pollingInterval
* The pollingInterval to set.
*/
public synchronized void setPollingInterval(long pollingInterval) {
this.pollingInterval = pollingInterval;
}
public int getFileHistorySize() {
return fileHistory.size();
}
/**
* @oddjob.property
* @oddjob.description How many lines to keep in file history.
* @oddjob.required No.
*/
public void setFileHistorySize(int fileHistorySize) {
this.fileHistory.setListSize(fileHistorySize);
}
public ScreenPresence getScreen() {
return screen;
}
public String getLogFormat() {
return logFormat;
}
public void setLogFormat(String logFormat) {
this.logFormat = logFormat;
}
private void open(File file) {
try {
Oddjob newJob = newOddjob();
newJob.setFile(file);
newJob.load();
setOddjob(newJob);
}
catch (PropertyVetoException e1) {
logger.info("Why?");
}
catch (RuntimeException ex) {
logger().warn("Exception opening file [" + file + "]", ex);
JOptionPane.showMessageDialog(frame, ex.getMessage(), "Exception!", JOptionPane.ERROR_MESSAGE);
}
}
public File getFile() {
return file;
}
@ArooaAttribute
public void setFile(File file) {
this.file = file;
}
}