简体   繁体   中英

Java programs runs in eclipse. Fails as runnable Jar

I've written a Java app that works fine when run from Eclipse The Compiler JDK is 1.8, the JRE is the one that came with jdk1.8.0_172 Eclipse is running it's normal 64 bit mode, the JDK is the 64 bit version

I need to be able to run the app from a .bat or .cmd file:

jre8\bin\java.exe -jar ClientNavigator.jar
pause

The path refers to a copy of the JRE which I will be bundling into an installer with the runnable .Jar

Running the .bat file results in this error:

jre8\bin\java.exe -jar ClientNavigator.jar
java.lang.NullPointerException
        at ClientNavigator.<init>(ClientNavigator.java:50)
        at ClientNavigator.main(ClientNavigator.java:61)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:58)

When I use eclipse to export the app as a runnable .jar I choose "Package required libraries into generated JAR"

Below is most of my Java code.

/*
 * Last edit: 5/9/2018 - Simon
*/

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Scanner;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.events.IHyperlinkListener;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Hyperlink;
import org.eclipse.wb.swt.SWTResourceManager;

public class ClientNavigator {

    protected Shell shlClientNavigator; // This is the main window
    private Text nameBox;
    private Text serverBox;
    private Text portBox;
    private final FormToolkit formToolkit = new FormToolkit(Display.getDefault());
    private String dataPath = ClientNavigator.class.getResource("BMCliDat.csv").toString().substring(5);
    public String dataFile = dataPath; // this is the data file. It's .csv for
                                        // easy export and edit

    /**
     * Launch the application.
     * 
     * @param args
     */
    public static void main(String[] args) {
        try {
            ClientNavigator window = new ClientNavigator();
            window.open();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Open the window.
     */
    public void open() {
        Display display = Display.getDefault();
        createContents();
        shlClientNavigator.open();
        shlClientNavigator.layout();
        while (!shlClientNavigator.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }

    /**
     * Create contents of the window.
     */
    protected void createContents() {
        shlClientNavigator = new Shell();
        String imgPath = ClientNavigator.class.getResource("BMInstaller.png").toString().substring(5);
        shlClientNavigator.setImage(SWTResourceManager.getImage(imgPath)); // non-essential
        shlClientNavigator.setMinimumSize(new Point(500, 500)); // prevent squishing
        shlClientNavigator.setSize(500, 500); // due to layout there is no benefit to stretching
        shlClientNavigator.setText("Client Navigator");
        // Style: try to keep all elements 10px apart
        Label lblName = new Label(shlClientNavigator, SWT.NONE);
        lblName.setBounds(10, 10, 60, 30);
        lblName.setText("Title:");

        Label lblName_1 = new Label(shlClientNavigator, SWT.NONE);
        lblName_1.setText("Server:");
        lblName_1.setBounds(10, 46, 60, 30);

        Label lblName_2 = new Label(shlClientNavigator, SWT.NONE);
        lblName_2.setText("Port:");
        lblName_2.setBounds(10, 81, 60, 30);

        final Label lblErrLabel = new Label(shlClientNavigator, SWT.WRAP | SWT.SHADOW_NONE);
        lblErrLabel.setTouchEnabled(true);
        lblErrLabel.setToolTipText("Error Bar");
        lblErrLabel.setForeground(SWTResourceManager.getColor(SWT.COLOR_RED));
        lblErrLabel.setBounds(10, 365, 458, 27);
        formToolkit.adapt(lblErrLabel, true, true);

        nameBox = new Text(shlClientNavigator, SWT.BORDER);
        nameBox.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.keyCode == 13) { // keyCode 13 on Windows is [Enter]. 9 is [Tab]
                    bmConnect(serverBox.getText().trim(), portBox.getText().trim(), nameBox.getText().trim());
                }
            }
        });
        nameBox.setToolTipText("Connection name");
        nameBox.setBounds(76, 10, 239, 30);

        serverBox = new Text(shlClientNavigator, SWT.BORDER);
        serverBox.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.keyCode == 13) { // keyCode 13 on Windows is [Enter]
                    bmConnect(serverBox.getText().trim(), portBox.getText().trim(), nameBox.getText().trim());
                }
            }
        });
        serverBox.setToolTipText("Server name");
        serverBox.setBounds(76, 46, 239, 30);

        portBox = new Text(shlClientNavigator, SWT.BORDER);
        portBox.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) { // keyCode 13 on Windows is [Enter]
                if (e.keyCode == 13) {
                    bmConnect(serverBox.getText().trim(), portBox.getText().trim(), nameBox.getText().trim());
                }
            }
        });
        portBox.setToolTipText("Port number");
        portBox.setBounds(76, 82, 239, 30);
        // The list is merely a display and does not hold any connection info
        final List list = new List(shlClientNavigator, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
        list.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.keyCode == 13) { // keyCode 13 on Windows is [Enter]
                    bmConnect(serverBox.getText().trim(), portBox.getText().trim(), nameBox.getText().trim());
                } else if (e.keyCode == 127) { // keyCode 127 on Windows is [Delete]
                    // fast delete with delete key - bad idea?
                    deleteData(nameBox.getText());
                    // repopulate the list
                    fillList(list);
                }
            }
        });
        list.addSelectionListener(new SelectionAdapter() {
            /*
             * What happens here: The SelectionEvent does not contain the info we need to
             * populate the textBoxes However, due to how the list is populated - see
             * fillList() - the SelectionIndecies match perfectly with the line numbers in
             * the .csv
             * 
             * So, the bufferedReader cycles down until the index of the selected item ==
             * the current .csv line number being read the line is then parsed and used to
             * populate the textBoxes
             */
            @Override
            public void widgetSelected(SelectionEvent e) {
                int lineNum = 0;
                String currLine;
                try {
                    FileReader fileReader = new FileReader(dataFile);
                    BufferedReader bufferedReader = new BufferedReader(fileReader);
                    while ((currLine = bufferedReader.readLine()) != null) {
                        String[] record = currLine.split(",");
                        if (lineNum == list.getSelectionIndex()) {
                            serverBox.setText(record[0]);
                            portBox.setText(record[1]);
                            nameBox.setText(record[2]);
                        }
                        lineNum++;
                    }
                    bufferedReader.close();
                } catch (FileNotFoundException ex) {
                    makeErrBar("Unable to open file '" + dataFile + "'", lblErrLabel);
                } catch (IOException ex) {
                    makeErrBar("Error reading file '" + dataFile + "'", lblErrLabel);
                }
            }
        });
        list.setBounds(10, 125, 458, 234);
        // initial list population
        fillList(list);
        formToolkit.adapt(list, true, true);

        Button btnGo = new Button(shlClientNavigator, SWT.CENTER);
        btnGo.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDown(MouseEvent e) {
                if (hasMissing((serverBox.getText().trim() + "," + portBox.getText().trim() + ","
                        + nameBox.getText().trim()))) {
                    makeErrBar("All fields are required", lblErrLabel);
                    return;
                }
                bmConnect(serverBox.getText().trim(), portBox.getText().trim(), nameBox.getText().trim());
            }
        });
        btnGo.setToolTipText("Establish connection");
        btnGo.setBounds(321, 9, 147, 52);
        btnGo.setText("Connect");

        Button btnSave = new Button(shlClientNavigator, SWT.CENTER);
        btnSave.setToolTipText("Save by title");
        btnSave.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDown(MouseEvent e) {
                String recordSave = (serverBox.getText().trim() + "," + portBox.getText().trim() + ","
                        + nameBox.getText().trim());
                if (hasMissing(recordSave)) {
                    makeErrBar("All fields are required", lblErrLabel);
                } else {
                    // see saveData() for more info on how this works
                    saveData(recordSave);
                    // repopulate the list
                    fillList(list);
                }
            }
        });
        btnSave.setText("Save");
        btnSave.setBounds(321, 67, 147, 52);

        Button btnDelete = new Button(shlClientNavigator, SWT.CENTER);
        btnDelete.setToolTipText("Remove saved connection");
        btnDelete.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                // Clicking the delete button spawns a new shell with a prompt to prevent
                // accidental deletions
                if (nameBox.getText().trim().length() < 1) {
                    makeErrBar("Title required", lblErrLabel);
                    return;
                } else {
                    final Shell shlDelDiag = new Shell();
                    shlDelDiag.setText("Delete " + nameBox.getText() + "?");
                    shlDelDiag.setSize(300, 200);

                    Label lblDiagLabel = new Label(shlDelDiag, SWT.WRAP);
                    // has enough space for substantially sized names but may not be enough
                    lblDiagLabel.setBounds(10, 10, 258, 77);
                    lblDiagLabel.setText("Are you sure you want to delete: " + nameBox.getText());

                    Button btnYes = new Button(shlDelDiag, SWT.NONE);
                    btnYes.addMouseListener(new MouseAdapter() {
                        @Override
                        public void mouseDown(MouseEvent e) {
                            // see deleteData for more info
                            deleteData(nameBox.getText());
                            // repopulate the list
                            fillList(list);
                            // dialogue box no longer needed
                            shlDelDiag.dispose();
                        }
                    });
                    btnYes.setBounds(163, 99, 105, 35);
                    btnYes.setText("Yes");

                    Button btnNo = new Button(shlDelDiag, SWT.NONE);
                    btnNo.addMouseListener(new MouseAdapter() {
                        @Override
                        public void mouseDown(MouseEvent e) {
                            // dialogue box no longer needed
                            shlDelDiag.dispose();
                        }
                    });
                    btnNo.setBounds(10, 99, 105, 35);
                    btnNo.setText("No");
                    shlDelDiag.open();
                    shlDelDiag.layout();
                    // possibly redundant?
                    fillList(list);
                }
            }
        });
        btnDelete.setText("Delete");
        btnDelete.setBounds(367, 398, 101, 36);
    }

    private void fillList(List list) {
        // fill out list w/ info
        String currLine = null;
        list.removeAll();
        try {
            // FileReader reads text files in the default encoding.
            FileReader fileReader = new FileReader(dataFile);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            while ((currLine = bufferedReader.readLine()) != null) {
                // use comma as separator
                String[] record = currLine.split(",");
                if (hasMissing(currLine)) {
                    continue;
                }
                list.add(record[2].toString()); // save by title
            }
            // Close files.
            bufferedReader.close();
        } catch (FileNotFoundException ex) {
            makeErrBar("Unable to open file '" + dataFile + "'", null);
        } catch (IOException ex) {
            makeErrBar("Error reading file '" + dataFile + "'", null);
        }
    }

    // This method creates a label containing relevant error messages
    private void makeErrBar(String error, Label lblErrLabel) {
        // If the error message is too large to fit spawn a new window.
        // The general idea here is that the smaller messages should be meaningful to
        // the end users while the larger messages are for debugging or tech support
        if (error.length() > 40 || lblErrLabel == null) {
            final Shell shlErrDiag = new Shell();
            shlErrDiag.setText("Error");
            shlErrDiag.setSize(300, 200);
            shlErrDiag.setLayout(new FillLayout(SWT.HORIZONTAL));
            Label lblDErrLabel = new Label(shlErrDiag, SWT.WRAP | SWT.SHADOW_NONE);
            lblDErrLabel.setTouchEnabled(true);
            lblDErrLabel.setToolTipText("Error Bar");
            lblDErrLabel.setText(error);
            lblDErrLabel.setForeground(SWTResourceManager.getColor(SWT.COLOR_RED));
            formToolkit.adapt(lblDErrLabel, true, true);
            shlErrDiag.open();
            shlErrDiag.layout();
        } else {
            lblErrLabel.setText(error);
            lblErrLabel.redraw();
            formToolkit.adapt(lblErrLabel, true, true);
            lblErrLabel.redraw();
        }
        // note that once the user triggers an error only another error will remove the
        // error label. Change?
    }

    private void saveData(String record) {
        String[] target = record.split(",");
        // This step is to prevent the creation of duplicates
        deleteData(target[2]); // 3rd index (target[2]) holds the name. delete by name
        try {
            FileWriter fileWriter = new FileWriter(dataFile, true);
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
            bufferedWriter.write(record);
            bufferedWriter.newLine();
            bufferedWriter.close();
        } catch (IOException ex) {
            makeErrBar("Error writing to file '" + dataFile + "'", null);
        }
    }

    // shortcut method to check for incomplete records in the .csv
    private boolean hasMissing(String rec) {
        if (rec.substring(0, 1).equals(",") || rec.substring(rec.length() - 1).equals(",") || rec.contains(",,")) {
            return true;
        }
        return false;
    }

    private void deleteData(String target) {
        File dFile = new File(dataFile);
        File tempFile = new File("temp.csv");
        try {
            // deletion is performed by writing a second file which excludes target
            BufferedReader reader = new BufferedReader(new FileReader(dFile));
            BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile));
            String currentLine;
            while ((currentLine = reader.readLine()) != null) {
                if (hasMissing(currentLine)) { // clean up blanks
                    continue;
                }
                String[] record = currentLine.split(",");
                if (record[2].equals(target))
                    continue;
                writer.write(record[0] + "," + record[1] + "," + record[2]);
                writer.newLine();
            }
            writer.close();
            reader.close();
            // delete original file
            dFile.delete();
            // temp takes it's name
            tempFile.renameTo(dFile);
            // temp file is deleted
            tempFile.delete();
        } catch (Exception e) {
            makeErrBar(e.toString(), null);
        }
    }

    @SuppressWarnings("resource")
    public void bmConnect(String server, String port, String title) {
        if (hasMissing(server + "," + port + "," + title)) {
            makeErrBar("All fields are required", null);
            return;
        } else {
            String bmConnectPath = ClientNavigator.class.getResource("bmConnect.bat").toString().substring(5);
            ProcessBuilder pb = new ProcessBuilder(bmConnectPath, title, server, port);
            Process process;
            try {
                process = pb.start();
                int errCode = process.waitFor();
                if (errCode != 0) {
                    InputStream errStream = process.getErrorStream();
                    Scanner errScan = new Scanner(errStream).useDelimiter("\\A");
                    String errMsg = errScan.hasNext() ? errScan.next() : "";
                    errScan.close();
                    errStream.close();
                    makeErrBar(errMsg, null);
                }
            } catch (IOException e) {
                makeErrBar(e.toString(), null);
            } catch (InterruptedException e) {
                makeErrBar(e.toString(), null);
            }
        }
    }
}

What do I need to change in order to get a runnable .Jar which I can package together with the jre?

Is there something wrong with my code? It compiles fine in Eclipse.

Could there be something wrong with the way I built my project?

I realize that my code heavily relies on the org.eclipse library. Is this the problem? If so, what should I do to remedy it?

Eclipse's export does not package additional files into the .Jar

As some users pointed out, the error refered to the fact that in

private String dataPath = ClientNavigator.class.getResource("BMCliDat.csv").toString().substring(5);

BMCliDat.csv did not exist.

The solution was to export the runnable .Jar and then manually add the requisite additional files to the directory that the .Jar lives in. Adding them directly to the .Jar did not work.

Thanks to Andres for the help!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM