be.ibridge.kettle.chef.ChefLog.java Source code

Java tutorial

Introduction

Here is the source code for be.ibridge.kettle.chef.ChefLog.java

Source

/**********************************************************************
 **                                                                   **
 **               This code belongs to the KETTLE project.            **
 **                                                                   **
 ** Kettle, from version 2.2 on, is released into the public domain   **
 ** under the Lesser GNU Public License (LGPL).                       **
 **                                                                   **
 ** For more details, please read the document LICENSE.txt, included  **
 ** in this project                                                   **
 **                                                                   **
 ** http://www.kettle.be                                              **
 ** info@kettle.be                                                    **
 **                                                                   **
 **********************************************************************/

package be.ibridge.kettle.chef;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;

import be.ibridge.kettle.core.Const;
import be.ibridge.kettle.core.LocalVariables;
import be.ibridge.kettle.core.LogWriter;
import be.ibridge.kettle.core.Props;
import be.ibridge.kettle.core.Result;
import be.ibridge.kettle.core.dialog.EnterSelectionDialog;
import be.ibridge.kettle.core.dialog.ErrorDialog;
import be.ibridge.kettle.core.exception.KettleException;
import be.ibridge.kettle.core.exception.KettleJobException;
import be.ibridge.kettle.core.widget.TreeMemory;
import be.ibridge.kettle.job.Job;
import be.ibridge.kettle.job.JobEntryResult;
import be.ibridge.kettle.job.JobMeta;
import be.ibridge.kettle.job.entry.JobEntryCopy;
import be.ibridge.kettle.spoon.Spoon;
import be.ibridge.kettle.spoon.TabItemInterface;
import be.ibridge.kettle.spoon.dialog.LogSettingsDialog;

/*** 
 * Handles the display of the job execution log in the Chef application.
 * 
 * @author Matt
 * @since 17-05-2003
 *
 */
public class ChefLog extends Composite implements TabItemInterface {
    private Color white;
    private Shell shell;
    private Display display;
    private LogWriter log;
    private Spoon spoon;
    private JobMeta jobMeta;

    private Tree wTree;

    private Text wText;
    private Button wStart;
    private Button wStop;
    private Button wRefresh;
    private Button wError;
    private Button wClear;
    private Button wLog;
    private Button wAuto;

    private FormData fdText, fdSash, fdStart, fdStop, fdRefresh, fdError, fdClear, fdLog, fdAuto;
    private SelectionListener lsStart, lsStop, lsRefresh, lsError, lsClear, lsLog;
    private StringBuffer message;

    private FileInputStream in;
    private Job job;
    private int previousNrItems;
    private boolean isRunning;
    private JobTracker jobTracker;
    private ChefHistoryRefresher chefHistoryRefresher;

    private static final String STRING_CHEF_LOG_TREE_NAME = "Job Log Tree";

    public ChefLog(Composite parent, final Spoon spoon, final JobMeta jobMeta) {
        super(parent, SWT.NONE);

        shell = parent.getShell();
        this.log = LogWriter.getInstance();
        display = shell.getDisplay();
        this.spoon = spoon;
        this.jobMeta = jobMeta;

        FormLayout formLayout = new FormLayout();
        formLayout.marginWidth = Const.FORM_MARGIN;
        formLayout.marginHeight = Const.FORM_MARGIN;

        setLayout(formLayout);

        setVisible(true);
        white = Display.getCurrent().getSystemColor(SWT.COLOR_WHITE);
        addDisposeListener(new DisposeListener() {
            public void widgetDisposed(DisposeEvent e) {
                white.dispose();
            }
        });

        SashForm sash = new SashForm(this, SWT.VERTICAL);

        // Create the tree table...
        wTree = new Tree(sash, SWT.V_SCROLL | SWT.H_SCROLL);
        wTree.setHeaderVisible(true);
        TreeMemory.addTreeListener(wTree, STRING_CHEF_LOG_TREE_NAME);

        TreeColumn column1 = new TreeColumn(wTree, SWT.LEFT);
        column1.setText(Messages.getString("ChefLog.Column.JobJobEntry")); //$NON-NLS-1$
        column1.setWidth(200);

        TreeColumn column2 = new TreeColumn(wTree, SWT.LEFT);
        column2.setText(Messages.getString("ChefLog.Column.Comment")); //$NON-NLS-1$
        column2.setWidth(200);

        TreeColumn column3 = new TreeColumn(wTree, SWT.LEFT);
        column3.setText(Messages.getString("ChefLog.Column.Result")); //$NON-NLS-1$
        column3.setWidth(100);

        TreeColumn column4 = new TreeColumn(wTree, SWT.LEFT);
        column4.setText(Messages.getString("ChefLog.Column.Reason")); //$NON-NLS-1$
        column4.setWidth(200);

        TreeColumn column5 = new TreeColumn(wTree, SWT.LEFT);
        column5.setText(Messages.getString("ChefLog.Column.Filename")); //$NON-NLS-1$
        column5.setWidth(200);

        TreeColumn column6 = new TreeColumn(wTree, SWT.RIGHT);
        column6.setText(Messages.getString("ChefLog.Column.Nr")); //$NON-NLS-1$
        column6.setWidth(50);

        TreeColumn column7 = new TreeColumn(wTree, SWT.RIGHT);
        column7.setText(Messages.getString("ChefLog.Column.LogDate")); //$NON-NLS-1$
        column7.setWidth(120);

        FormData fdTable = new FormData();
        fdTable.left = new FormAttachment(0, 0);
        fdTable.top = new FormAttachment(0, 0);
        fdTable.right = new FormAttachment(100, 0);
        fdTable.bottom = new FormAttachment(100, 0);
        wTree.setLayoutData(fdTable);

        wText = new Text(sash, SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL | SWT.READ_ONLY);
        wText.setBackground(white);
        wText.setVisible(true);

        wStart = new Button(this, SWT.PUSH);
        wStart.setText(Messages.getString("ChefLog.Button.Start")); //$NON-NLS-1$

        wStop = new Button(this, SWT.PUSH);
        wStop.setText(Messages.getString("ChefLog.Button.Stop")); //$NON-NLS-1$

        wRefresh = new Button(this, SWT.PUSH);
        wRefresh.setText(Messages.getString("ChefLog.Button.RefreshLog")); //$NON-NLS-1$

        wError = new Button(this, SWT.PUSH);
        wError.setText(Messages.getString("ChefLog.Button.ShowErrorLines")); //$NON-NLS-1$

        wClear = new Button(this, SWT.PUSH);
        wClear.setText(Messages.getString("ChefLog.Button.ClearLog")); //$NON-NLS-1$

        wLog = new Button(this, SWT.PUSH);
        wLog.setText(Messages.getString("ChefLog.Button.LogSettings")); //$NON-NLS-1$

        wAuto = new Button(this, SWT.CHECK);
        wAuto.setText(Messages.getString("ChefLog.Button.AutoRefresh")); //$NON-NLS-1$
        wAuto.setSelection(true);

        enableFields();

        fdStart = new FormData();
        fdStop = new FormData();
        fdRefresh = new FormData();
        fdError = new FormData();
        fdClear = new FormData();
        fdLog = new FormData();
        fdAuto = new FormData();

        fdStart.left = new FormAttachment(0, 10);
        fdStart.bottom = new FormAttachment(100, 0);
        wStart.setLayoutData(fdStart);

        fdStop.left = new FormAttachment(wStart, 10);
        fdStop.bottom = new FormAttachment(100, 0);
        wStop.setLayoutData(fdStop);

        fdRefresh.left = new FormAttachment(wStop, 10);
        fdRefresh.bottom = new FormAttachment(100, 0);
        wRefresh.setLayoutData(fdRefresh);

        fdError.left = new FormAttachment(wRefresh, 10);
        fdError.bottom = new FormAttachment(100, 0);
        wError.setLayoutData(fdError);

        fdClear.left = new FormAttachment(wError, 10);
        fdClear.bottom = new FormAttachment(100, 0);
        wClear.setLayoutData(fdClear);

        fdLog.left = new FormAttachment(wClear, 10);
        fdLog.bottom = new FormAttachment(100, 0);
        wLog.setLayoutData(fdLog);

        fdAuto.left = new FormAttachment(wLog, 10);
        fdAuto.bottom = new FormAttachment(100, 0);
        wAuto.setLayoutData(fdAuto);

        fdText = new FormData();
        fdText.left = new FormAttachment(0, 0);
        fdText.top = new FormAttachment(0, 0);
        fdText.right = new FormAttachment(100, 0);
        fdText.bottom = new FormAttachment(100, -30);
        wText.setLayoutData(fdText);

        fdSash = new FormData();
        fdSash.left = new FormAttachment(0, 0); // First one in the left top corner
        fdSash.top = new FormAttachment(0, 0);
        fdSash.right = new FormAttachment(100, 0);
        fdSash.bottom = new FormAttachment(wStart, -5);
        sash.setLayoutData(fdSash);

        sash.setWeights(new int[] { 60, 40 });

        pack();

        try {
            in = log.getFileInputStream();
        } catch (Exception e) {
            System.out.println(Messages.getString("ChefLog.Error.CouldNotCreateInputPipe")); //$NON-NLS-1$
        }

        lsRefresh = new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                readLog();
                checkEnded();
            }
        };

        final Timer tim = new Timer();
        TimerTask timtask = new TimerTask() {
            public void run() {
                if (display != null && !display.isDisposed())
                    display.asyncExec(new Runnable() {
                        public void run() {
                            // Chef if the widgets are not disposed.  
                            // This happens is the rest of the window is not yet disposed.
                            // We ARE running in a different thread after all.
                            //
                            if (!wAuto.isDisposed() && !wText.isDisposed() && !wStart.isDisposed()
                                    && !wTree.isDisposed()) {
                                if (wAuto.getSelection()) {
                                    readLog();
                                    checkEnded();
                                }
                            }
                        }
                    });
            }
        };
        tim.schedule(timtask, 10L, 10L);// refresh every 2 seconds... 

        lsStart = new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                startJob();
            }
        };
        lsStop = new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                stopJob();
            }
        };
        lsError = new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                showErrors();
            }
        };
        lsClear = new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                clearLog();
            }
        };
        lsLog = new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                setLog();
            }
        };

        wRefresh.addSelectionListener(lsRefresh);
        wStart.addSelectionListener(lsStart);
        wStop.addSelectionListener(lsStop);
        wError.addSelectionListener(lsError);
        wClear.addSelectionListener(lsClear);
        wLog.addSelectionListener(lsLog);

        addDisposeListener(new DisposeListener() {
            public void widgetDisposed(DisposeEvent e) {
                tim.cancel();
            }
        });
    }

    public synchronized void startJob() {
        startJob(null);
    }

    public synchronized void startJob(Date replayDate) {
        if (job == null) // Not running, start the transformation...
        {
            // Auto save feature...
            if (jobMeta.hasChanged()) {
                if (spoon.props.getAutoSave()) {
                    log.logDetailed(toString(), Messages.getString("ChefLog.Log.AutoSaveFileBeforeRunning")); //$NON-NLS-1$
                    System.out.println(Messages.getString("ChefLog.Log.AutoSaveFileBeforeRunning2")); //$NON-NLS-1$
                    spoon.saveJobFile(jobMeta);
                } else {
                    MessageDialogWithToggle md = new MessageDialogWithToggle(shell,
                            Messages.getString("ChefLog.Dialog.SaveChangedFile.Title"), //$NON-NLS-1$
                            null,
                            Messages.getString("ChefLog.Dialog.SaveChangedFile.Message") + Const.CR //$NON-NLS-1$
                                    + Messages.getString("ChefLog.Dialog.SaveChangedFile.Message2") + Const.CR, //$NON-NLS-1$
                            MessageDialog.QUESTION,
                            new String[] { Messages.getString("System.Button.Yes"), //$NON-NLS-1$
                                    Messages.getString("System.Button.No") }, //$NON-NLS-1$
                            0, Messages.getString("ChefLog.Dialog.SaveChangedFile.Toggle"), //$NON-NLS-1$
                            spoon.props.getAutoSave());
                    int answer = md.open();
                    if ((answer & 0xFF) == 0) {
                        spoon.saveJobFile(jobMeta);
                    }
                    spoon.props.setAutoSave(md.getToggleState());
                }
            }

            if (((jobMeta.getName() != null && spoon.rep != null) || // Repository available & name set
                    (jobMeta.getFilename() != null && spoon.rep == null) // No repository & filename set
            ) && !jobMeta.hasChanged() // Didn't change
            ) {
                if (job == null || (job != null && job.isActive())) {
                    try {
                        // TODO: clean up this awfull mess...
                        //
                        job = new Job(log, jobMeta.getName(), jobMeta.getFilename(), null);
                        job.open(spoon.rep, jobMeta.getFilename(), jobMeta.getName(),
                                jobMeta.getDirectory().getPath());
                        job.getJobMeta().setArguments(jobMeta.getArguments());

                        log.logMinimal(Chef.APP_NAME, Messages.getString("ChefLog.Log.StartingJob")); //$NON-NLS-1$
                        job.start();
                        // Link to the new jobTracker!
                        jobTracker = job.getJobTracker();
                        isRunning = true;
                    } catch (KettleException e) {
                        new ErrorDialog(shell, Messages.getString("ChefLog.Dialog.CanNotOpenJob.Title"), //$NON-NLS-1$
                                Messages.getString("ChefLog.Dialog.CanNotOpenJob.Message"), e); //$NON-NLS-1$
                        job = null;
                    }
                } else {
                    MessageBox m = new MessageBox(shell, SWT.OK | SWT.ICON_WARNING);
                    m.setText(Messages.getString("ChefLog.Dialog.JobIsAlreadyRunning.Title")); //$NON-NLS-1$
                    m.setMessage(Messages.getString("ChefLog.Dialog.JobIsAlreadyRunning.Message")); //$NON-NLS-1$
                    m.open();
                }
            } else {
                if (jobMeta.hasChanged()) {
                    MessageBox m = new MessageBox(shell, SWT.OK | SWT.ICON_WARNING);
                    m.setText(Messages.getString("ChefLog.Dialog.JobHasChangedSave.Title")); //$NON-NLS-1$
                    m.setMessage(Messages.getString("ChefLog.Dialog.JobHasChangedSave.Message")); //$NON-NLS-1$
                    m.open();
                } else if (spoon.rep != null && jobMeta.getName() == null) {
                    MessageBox m = new MessageBox(shell, SWT.OK | SWT.ICON_WARNING);
                    m.setText(Messages.getString("ChefLog.Dialog.PleaseGiveThisJobAName.Title")); //$NON-NLS-1$
                    m.setMessage(Messages.getString("ChefLog.Dialog.PleaseGiveThisJobAName.Message")); //$NON-NLS-1$
                    m.open();
                } else {
                    MessageBox m = new MessageBox(shell, SWT.OK | SWT.ICON_WARNING);
                    m.setText(Messages.getString("ChefLog.Dialog.NoFilenameSaveYourJobFirst.Title")); //$NON-NLS-1$
                    m.setMessage(Messages.getString("ChefLog.Dialog.NoFilenameSaveYourJobFirst.Message")); //$NON-NLS-1$
                    m.open();
                }
            }
            enableFields();
        }
    }

    private synchronized void stopJob() {
        try {
            if (job != null && isRunning && job.isInitialized()) {
                job.stopAll();
                job.endProcessing("stop", new Result()); //$NON-NLS-1$
                job.waitUntilFinished(5000); // wait until everything is stopped, maximum 5 seconds...
                LocalVariables.getInstance().removeKettleVariables(job.getName());
                job = null;
                isRunning = false;
                log.logMinimal(Chef.APP_NAME, Messages.getString("ChefLog.Log.JobWasStopped")); //$NON-NLS-1$
            }
        } catch (KettleJobException je) {
            MessageBox m = new MessageBox(shell, SWT.OK | SWT.ICON_WARNING);
            m.setText(Messages.getString("ChefLog.Dialog.UnableToSaveStopLineInLoggingTable.Title")); //$NON-NLS-1$
            m.setMessage(Messages.getString("ChefLog.Dialog.UnableToSaveStopLineInLoggingTable.Message") + Const.CR //$NON-NLS-1$
                    + je.toString());
            m.open();
        } finally {
            enableFields();
        }
    }

    public void enableFields() {
        wStart.setEnabled(!isRunning);
        wStop.setEnabled(isRunning);
        // spoon.tiFileRun.setEnabled(!isRunning); TODO: make spoons menu's smarter
    }

    public void readLog() {
        if (message == null)
            message = new StringBuffer();
        else
            message.setLength(0);
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(in, Const.XML_ENCODING));
            String line;
            while ((line = reader.readLine()) != null) {
                message.append(line);
                message.append(Const.CR);
            }
            refreshTreeTable();
        } catch (Exception ex) {
            message.append(ex.toString());
        }

        if (!wText.isDisposed() && message.length() > 0) {
            wText.setSelection(wText.getText().length());
            wText.clearSelection();
            wText.insert(message.toString());

            int maxLines = Props.getInstance().getMaxNrLinesInLog();
            if (maxLines > 0 && wText.getLineCount() > maxLines) {
                // OK, remove the extra amount of character + 20 from 
                // Remove the oldest ones.
                StringBuffer buffer = new StringBuffer(wText.getText());
                buffer.delete(0, message.length() + 20);
                wText.setText(buffer.toString());
            }
        }
    }

    /**
     * Refresh the data in the tree-table...
     * Use the data from the JobTracker in the job
     */
    private void refreshTreeTable() {
        if (jobTracker != null) {
            int nrItems = jobTracker.getTotalNumberOfItems();

            if (nrItems != previousNrItems) {
                // Allow some flickering for now ;-)
                wTree.removeAll();

                // Re-populate this...
                TreeItem treeItem = new TreeItem(wTree, SWT.NONE);
                String jobName = jobTracker.getJobName();

                if (Const.isEmpty(jobName)) {
                    if (!Const.isEmpty(jobTracker.getJobFilename()))
                        jobName = jobTracker.getJobFilename();
                    else
                        jobName = Messages.getString("ChefLog.Tree.StringToDisplayWhenJobHasNoName"); //$NON-NLS-1$
                }
                treeItem.setText(0, jobName);
                TreeMemory.getInstance().storeExpanded(STRING_CHEF_LOG_TREE_NAME, new String[] { jobName }, true);

                for (int i = 0; i < jobTracker.nrJobTrackers(); i++) {
                    addTrackerToTree(jobTracker.getJobTracker(i), treeItem);
                }
                previousNrItems = nrItems;

                TreeMemory.setExpandedFromMemory(wTree, STRING_CHEF_LOG_TREE_NAME);
            }
        }
    }

    private void addTrackerToTree(JobTracker jobTracker, TreeItem parentItem) {
        try {
            if (jobTracker != null) {
                TreeItem treeItem = new TreeItem(parentItem, SWT.NONE);
                if (jobTracker.nrJobTrackers() > 0) {
                    // This is a sub-job: display the name at the top of the list...
                    treeItem.setText(0, Messages.getString("ChefLog.Tree.JobPrefix") + jobTracker.getJobName()); //$NON-NLS-1$

                    // then populare the sub-job entries ...
                    for (int i = 0; i < jobTracker.nrJobTrackers(); i++) {
                        addTrackerToTree(jobTracker.getJobTracker(i), treeItem);
                    }
                } else {
                    JobEntryResult result = jobTracker.getJobEntryResult();
                    if (result != null) {
                        JobEntryCopy jec = result.getJobEntry();
                        if (jec != null) {
                            treeItem.setText(0, jec.getName());

                            if (jec.getEntry() != null) {
                                treeItem.setText(4, Const.NVL(jec.getEntry().getRealFilename(), ""));
                            }
                        } else {
                            treeItem.setText(0,
                                    Messages.getString("ChefLog.Tree.JobPrefix2") + jobTracker.getJobName()); //$NON-NLS-1$
                        }
                        String comment = result.getComment();
                        if (comment != null) {
                            treeItem.setText(1, comment);
                        }
                        Result res = result.getResult();
                        if (res != null) {
                            treeItem.setText(2, res.getResult() ? Messages.getString("ChefLog.Tree.Success") //$NON-NLS-1$
                                    : Messages.getString("ChefLog.Tree.Failure")); //$NON-NLS-1$
                            treeItem.setText(5, "" + res.getEntryNr()); //$NON-NLS-1$
                        }
                        String reason = result.getReason();
                        if (reason != null) {
                            treeItem.setText(3, reason);
                        }
                        Date logDate = result.getLogDate();
                        if (logDate != null) {
                            treeItem.setText(6, new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(logDate)); //$NON-NLS-1$
                        }
                    }
                }
                treeItem.setExpanded(true);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private synchronized void checkEnded() {
        if (isRunning && job != null && job.isInitialized() && !job.isAlive()) {
            LocalVariables.getInstance().removeKettleVariables(job.getName());

            job = null;
            isRunning = false;
            if (chefHistoryRefresher != null)
                chefHistoryRefresher.markRefreshNeeded();
            log.logMinimal(Chef.APP_NAME, Messages.getString("ChefLog.Log.JobHasEnded")); //$NON-NLS-1$
        }

        if (!wStart.isDisposed()) {
            enableFields();
        }
    }

    private void clearLog() {
        wText.setText(""); //$NON-NLS-1$
    }

    private void setLog() {
        LogSettingsDialog lsd = new LogSettingsDialog(shell, SWT.NONE, log, spoon.props);
        lsd.open();
    }

    public String toString() {
        return this.getClass().getName();
    }

    public boolean isRunning() {
        return job != null;
    }

    public void showErrors() {
        String all = wText.getText();
        ArrayList err = new ArrayList();

        int i = 0;
        int startpos = 0;
        int crlen = Const.CR.length();

        while (i < all.length() - crlen) {
            if (all.substring(i, i + crlen).equalsIgnoreCase(Const.CR)) {
                String line = all.substring(startpos, i);
                if (line.toUpperCase().indexOf(Messages.getString("ChefLog.System.ERROR")) >= 0 || //$NON-NLS-1$
                        line.toUpperCase().indexOf(Messages.getString("ChefLog.System.EXCEPTION")) >= 0 //$NON-NLS-1$
                ) {
                    err.add(line);
                }
                // New start of line
                startpos = i + crlen;
            }

            i++;
        }
        String line = all.substring(startpos);
        if (line.toUpperCase().indexOf(Messages.getString("ChefLog.System.ERROR")) >= 0 || //$NON-NLS-1$
                line.toUpperCase().indexOf(Messages.getString("ChefLog.System.EXCEPTION")) >= 0 //$NON-NLS-1$
        ) {
            err.add(line);
        }

        if (err.size() > 0) {
            String err_lines[] = new String[err.size()];
            for (i = 0; i < err_lines.length; i++)
                err_lines[i] = (String) err.get(i);

            EnterSelectionDialog esd = new EnterSelectionDialog(shell, err_lines,
                    Messages.getString("ChefLog.Dialog.ErrorLines.Title"), //$NON-NLS-1$
                    Messages.getString("ChefLog.Dialog.ErrorLines.Message")); //$NON-NLS-1$
            line = esd.open();
            if (line != null) {
                for (i = 0; i < jobMeta.nrJobEntries(); i++) {
                    JobEntryCopy entryCopy = jobMeta.getJobEntry(i);
                    if (line.indexOf(entryCopy.getName()) >= 0) {
                        spoon.editJobEntry(jobMeta, entryCopy);
                    }
                }
                // System.out.println("Error line selected: "+line);
            }
        }
    }

    public void setChefHistoryRefresher(ChefHistoryRefresher chefHistoryRefresher) {
        this.chefHistoryRefresher = chefHistoryRefresher;
    }

    /**
     * @return the jobMeta
     */
    public JobMeta getJobMeta() {
        return jobMeta;
    }

    /**
     * @param jobMeta the jobMeta to set
     */
    public void setJobMeta(JobMeta jobMeta) {
        this.jobMeta = jobMeta;
    }

    public boolean applyChanges() {
        return true;
    }

    public boolean canBeClosed() {
        return !isRunning;
    }

    public Object getManagedObject() {
        return jobMeta;
    }

    public boolean hasContentChanged() {
        return false;
    }

    public int showChangedWarning() {
        // show running error.
        MessageBox mb = new MessageBox(shell, SWT.YES | SWT.NO | SWT.ICON_QUESTION);
        mb.setMessage(Messages.getString("ChefLog.Message.Warning.PromptExitWhenRunJob"));// There is a running job.  Do you want to stop it and quit Spoon?
        mb.setText(Messages.getString("System.Warning")); //Warning
        int answer = mb.open();
        if (answer == SWT.NO)
            return SWT.CANCEL;
        return answer;
    }
}