Java 程序在 eclipse 中运行.作为可运行的 Jar 失败



我写了一个Java应用程序,从Eclipse运行时工作正常 编译器JDK是1.8,JRE是jdk1.8.0_172附带的编译器 Eclipse正在运行正常的64位模式,JDK是64位版本

我需要能够从 .bat 或 .cmd 文件运行该应用程序:

jre8binjava.exe -jar ClientNavigator.jar
pause

该路径是指 JRE 的副本,我将将其捆绑到带有可运行的安装程序的安装程序中。罐

运行.bat文件会导致此错误:

jre8binjava.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)

当我使用 eclipse 将应用程序导出为可运行的.jar时,我选择"将所需的库打包到生成的 JAR 中">

下面是我的大部分 Java 代码。

/*
* 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);
}
}
}
}

我需要更改什么才能获得可运行的.我可以和jre一起包装的罐子?

我的代码有问题吗?它在 Eclipse 中编译得很好。

我构建项目的方式会不会有问题?

我意识到我的代码严重依赖org.eclipse库。这是问题所在吗?如果是这样,我应该怎么做才能补救?

Eclipse 的导出不会将其他文件打包到 .罐

正如一些用户指出的那样,该错误指的是以下事实:在

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

BMCliDat.csv不存在。

解决方案是导出可运行的.Jar,然后手动将必要的附加文件添加到 .贾尔住进去。将它们直接添加到 .罐子不起作用。

感谢安德烈斯的帮助!

最新更新