Why does my Swing worker thread interrupt work? - java

I have a small java program, written in Eclipse using WindowBuilder, which works on reading data from UTF-8 text files and writing them into a database. To maintain the GUI's responsiveness, I use a swing worker thread, executed when clicking on a button.
btnex.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
worker = new SwingWorker<Void, Void>() {
public Void doInBackground() {
String[] content = Reader.getContent(file);
//do something with the content, if something goes wrong, set error to true.
return null;
}
public void done() {
if (!error) {
//handle error
}
};
worker.execute();
The function getContent in the class Reader extracts data from the file into the string array.
public static String[] getContent (String dbfile) {
try {
String[] lines = null;
String[] linesplit = null;
String store = "";
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(dbfile), "UTF8"));
String line = "";
line = reader.readLine();
linesplit = line.split(";");
while (linesplit.length > 1 && !line.equals(null)) {
for (int i = 0; i < linesplit.size(); i++) {
store += StringFormer.decrypt(linesplit[i]) + " ";
} //StringFormer is another class written by me, just for decrypting the string
store += "\n";
line = reader.readLine();
if (line.equals(null)) break;
linesplit = line.split(";");
}
reader.close();
lines = store.split("\n");
return lines;
} catch (Exception ex) { //...
}
}
When I try to run my program and click the button, the program does not work correctly. So I run the program in debug mode, and as a result, the thead does not finish but somehow exits before finishing all work. This happens in getContent, after leaving the while loop but before working on reader.close(). Before leaving the while loop, the call stack in the debug view contains the calls of the button click and of getContent, next to others, but once I leave the loop, the two mentioned above are dropped and the next top stack member names the base swing worker class: SwingWorker$2(FutureTask).run.
Does anyone know, why the program does not finish the written work flow? I work with multiple threads in the program, but never two background threads are running at the same time.

I found the answer myself:
The line if(line.equals(null) throws a NullPointerException. line is null when nothing more can be read out of the file, but the function equals requires that the compared variable is defined. Since line was null, equals threw the exception without me noticing it. The line has to be if(line==null).

Related

Editing a file using async threads in Java

I'm a small java developer currently working on a discord bot that I made in Java. one of the features of my bot is to simply have a leveling system whenever anyone sends a message (and other conditions but this is irrelevant for the problem I'm encountering).
Whenever someone sends a message an event is fired and a thread is created to compute how much exp the user should gain. and eventually, the function to edit the storage file is called.
which works fine when called sparsely. but if two threads try to write on the file at once, the file usually gets deleted or truncated. either of these two cases being undesired behavior
I then tried to make a queuing system that worked for over 24h but still failed once so it is more stable in a way. I only know the basics of how threads work so I may've skipped over an important thing that causes the problem
the function looks like this
Thread editingThread = null;
public boolean editThreadStarted = false;
HashMap<String, String> queue = new HashMap<>();
public final boolean editParameter(String key, String value) {
queue.put(key, value);
if(!editThreadStarted) {
editingThread = new Thread(new Runnable() {
#Override
public void run() {
while(queue.keySet().size() > 0) {
String key = (String) queue.keySet().toArray()[0];
String value = queue.get(key);
File inputFile = getFile();
File tempFile = new File(getFile().getName() + ".temp");
try {
tempFile.createNewFile();
} catch (IOException e) {
DemiConsole.error("Failed to create temp file");
handleTrace(e);
continue;
}
//System.out.println("tempFile.isFile = " + tempFile.isFile());
try (BufferedReader reader = new BufferedReader(new FileReader(inputFile)); BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile))){
String currentLine;
while((currentLine = reader.readLine()) != null) {
String trimmedLine = currentLine.trim();
if(trimmedLine.startsWith(key)) {
writer.write(key + ":" + value + System.getProperty("line.separator"));
continue;
}
writer.write(currentLine + System.getProperty("line.separator"));
}
writer.close();
reader.close();
inputFile.delete();
tempFile.renameTo(inputFile);
} catch (IOException e) {
DemiConsole.error("Caught an IO exception while attempting to edit parameter ("+key+") in file ("+getFile().getName()+"), returning false");
handleTrace(e);
continue;
}
queue.remove(key);
}
editThreadStarted = false;
}
});
editThreadStarted = true;
editingThread.start();
}
return true;
}
getFile() returns the file the function is meant to write to
the file format is
memberid1:expamount
memberid2:expamount
memberid3:expamount
memberid4:expamount
the way the editing works is by creating a temporary file to which i will write all of the original file's data line by line, checking if the memberid matches with what i want to edit, if it does, then instead of writing the original file's line, i will write the new edited line with the new expamount instead, before continuing on with the rest of the lines. Once that is done, the original file is deleted and the temporary file is renamed to the original file, replacing it.
This function will always be called asynchronously so making the whole thing synchronous is not an option.
Thanks in advance
Edit(1) :
I've been suggested to use semaphores and after digging a little into it (i never heard of semaphores before) it seems to be a really good option and would remove the need for a queue, simply aquire in the beginning and release at the end, nothing more required!
I ended up using semaphores as per user207421's suggestions and it seems to work perfectly
I simply put delays between each line write to artificially make the task longer and make it easier to have multiple threads trying to write at once, and they all wait for their turns!
Thanks

Java variable not being affected

Now this may sound like a question that has been repeated many times before but I've done a day of research with people that has other reasons for this Issue.
I have a function that reads a part of the save file and its been shown that it does receive the correct data. So the error is that the integer variable completely ignores the new variable and shows no change in the live debugger so like many other post it is not just a duplicate object error. I cant seem to pinpoint what is the main issue is here and it's the last major thing holding me back. Any help would be great and I'm very extremely sorry if I did manage to miss a topic about this on the internet.
Code that fails:
#Override
public void read(List<String> data) {
//world positions are not being changed at all
System.out.println(data.get(1));
int test = Integer.valueOf(data.get(1).replaceAll("[^\\d.]", ""));
worldXPos = Integer.valueOf(data.get(0).replaceAll("[^\\d.]", ""));
worldZPos = test;
}
Another class that gives the data:
public void readSaveFunctions(){
if(!gameSaves.exists()){
gameSaves.mkdir();
}
String currentLine;
try {
List<String> data = new ArrayList<String>();
FileReader read = new FileReader(currentFile);
BufferedReader reader = new BufferedReader(read);
String key = "";
while((currentLine = reader.readLine()) != null){
if(currentLine.contains("#")){
key = currentLine;
data = new ArrayList<String>();
}else if(currentLine.contains("*end")){
for(int i = 0; i < saves.length; i++){
String tryKey = "#" + saves[i].IDName();
if(tryKey.equals(key)){
key = "";
saves[i].read(data);
}
}
}else data.add(currentLine);
}
reader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Another way of explaining it is this:
Debugger is set to step - to - step mode so I see each line getting executed at human speed then I get to a line like this but all of the ones setting the variables have the same effect:
worldXPos = Integer.valueOf(data.get(0).replaceAll("[^\\d.]", ""));
and the debugger shows the two integers having different numbers but the instant class variable stays exactly the same with no effect in the debugger after the line goes through.
Update:
I forgot to mention the method has a #override method and it seems that this #override may be causing this issue, now finally I may have a path to follow again
So I found my answer: The AWT thread manage to activate calling a method from another class that changed the integer before it could be read. It really though me off at first because the debugger only showed one of the threads and with no way to know the other one was actively changing it to early. Thanks for all the help :P.

Tracking the progress of wget (bash) with SwingWorker / Swing

I am trying to make a gui, whereby users are able to download files. Currently I am able to call the wget command through a process, but I am struggling to use it along with swingworker.
How would I go about tracking the progress of the downloading and updating a gui simultaneously?
Currently I have tried using this method:
ShellProcess.command("wget --progress=dot "+_url);
Where command is the method that creates the process:
InputStream stdout = _process.getInputStream();
BufferedReader stdoutBuffered =new BufferedReader(new InputStreamReader(stdout));
String line = null;
String output ="";
try {
while ((line = _stdoutBuffered.readLine()) != null ) {
// System.out.println(line);
output+=(line+" ");
System.out.println(line +" SHELL");
_progress++;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
_progress = 0;
return output;
}
I am trying to count the amount of lines outputted as "wget --progress=dot" should output a line for every percent of progress. But this does not seem to work.
My doInBackground method inside the swingworker looks like this:
#Override
protected Integer doInBackground() throws Exception {
// Start
download .command("wget "+_url);
ShellProcess.command("wget --progress=dot "+_url);
int progress = 0;
while (progress<101){
progress = ShellProcess.getProgress() %100 ;
publish(ShellProcess.getOutput());
setProgress(progress);
}
return 1;
}
Any help would be appreciated.
In this complete example, the background method of a SwingWorker starts a ProcessBuilder. Standard output and error streams are combined for display in a text component. Substitute your wget command to see the effect. Experiment with --progress=bar and reading a character at a time.
ProcessBuilder pb = new ProcessBuilder("wget", "--progress=dot", url);
You don't really need SwingWorker for this. Just download in a separate Thread (don't do it in the EDT), and every time you encounter a new dot line output from wget, update a GUI component (a progress bar for example), but do this update in the EDT e.g. with SwingUtilities.invokeLater():
JProgressBar progressBar = ...; // Initialize and add progress bar to your GUI
...
// In your separate download thread:
final AtomicInteger percent = new AtomicInteger();
while ((line = _stdoutBuffered.readLine()) != null ) {
if (".".equals(line)) {
// A new percent was completed, update the progressbar:
percent.incrementAndGet();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
progressBar.setValue(percent.get());
}
});
}
}

design for a wrapper around command-line utilities

im trying to come up with a design for a wrapper for use when invoking command line utilities in java. the trouble with runtime.exec() is that you need to keep reading from the process' out and err streams or it hangs when it fills its buffers. this has led me to the following design:
public class CommandLineInterface {
private final Thread stdOutThread;
private final Thread stdErrThread;
private final OutputStreamWriter stdin;
private final History history;
public CommandLineInterface(String command) throws IOException {
this.history = new History();
this.history.addEntry(new HistoryEntry(EntryTypeEnum.INPUT, command));
Process process = Runtime.getRuntime().exec(command);
stdin = new OutputStreamWriter(process.getOutputStream());
stdOutThread = new Thread(new Leech(process.getInputStream(), history, EntryTypeEnum.OUTPUT));
stdOutThread.setDaemon(true);
stdOutThread.start();
stdErrThread = new Thread(new Leech(process.getErrorStream(), history, EntryTypeEnum.ERROR));
stdErrThread.setDaemon(true);
stdErrThread.start();
}
public void write(String input) throws IOException {
this.history.addEntry(new HistoryEntry(EntryTypeEnum.INPUT, input));
stdin.write(input);
stdin.write("\n");
stdin.flush();
}
}
And
public class Leech implements Runnable{
private final InputStream stream;
private final History history;
private final EntryTypeEnum type;
private volatile boolean alive = true;
public Leech(InputStream stream, History history, EntryTypeEnum type) {
this.stream = stream;
this.history = history;
this.type = type;
}
public void run() {
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
String line;
try {
while(alive) {
line = reader.readLine();
if (line==null) break;
history.addEntry(new HistoryEntry(type, line));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
my issue is with the Leech class (used to "leech" the process' out and err streams and feed them into history - which acts like a log file) - on the one hand reading whole lines is nice and easy (and what im currently doing), but it means i miss the last line (usually the prompt line). i only see the prompt line when executing the next command (because there's no line break until that point).
on the other hand, if i read characters myself, how can i tell when the process is "done" ? (either complete or waiting for input)
has anyone tried something like waiting 100 millis since the last output from the process and declaring it "done" ?
any better ideas on how i can implement a nice wrapper around things like runtime.exec("cmd.exe") ?
Use PlexusUtils it is used by Apache Maven 2 to execute all external processes.
I was looking for the same thing myself, and I found a Java port of Expect, called ExpectJ. I haven't tried it yet, but it looks promising
I would read the input in with the stream and then write it into a ByteArrayOutputStream. The byte array will continue to grow until there are no longer any available bytes to read. At this point you will flush the data to history by converting the byte array into a String and splitting it on the platform line.separator. You can then iterate over the lines to add history entries. The ByteArrayOutputStream is then reset and the while loop blocks until there is more data or the end of stream is reached (probably because the process is done).
public void run() {
ByteArrayOutputStream out = new ByteArrayOutputStream();
int bite;
try {
while((bite = stream.read()) != -1) {
out.write(bite);
if (stream.available() == 0) {
String string = new String(out.toByteArray());
for (String line : string.split(
System.getProperty("line.separator"))) {
history.addEntry(new HistoryEntry(type, line));
}
out.reset();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
This will make sure you pick up that last line of input and it solves your problem of knowing when the stream is ended.

What is the best way, if possible, to send information from a Java PrintStream to a JTextPane?

In Java, I have a package that translates XML metadata from one standard to another. This package is ultimately accessed through a single function and sends all of its output through a PrintStream object. The output sent is just a status of each file and whether or not it was translated.
This is pretty fine and dandy if I'm just printing to System.out, but I'm actually wanting to print this to a JTextPane while it translates (kind of like a progress text box). It wouldn't be a big deal to just print the status after it was done translating the XML, but since there may be thousands of XML files, that's just not feasible.
One thing that I've tried is to use a thread that takes all of the information from the PrintStream (which is attached to a ByteArrayOutputStream) and let it send any new information to the text pane. Unfortunately, this still sends the information all at once at the end of the translation. This does work correctly for System.out.
Here's the code that does the translation and tries to show the output:
public class ConverterGUI extends javax.swing.JFrame {
boolean printToResultsBox = false;
PrintStream printStream = null;
ByteArrayOutputStream baos = null;
private class ResultsPrinter implements Runnable {
public ResultsPrinter() {
baos = new ByteArrayOutputStream();
printStream = new PrintStream(baos);
}
public void run() {
String tempString = "";
while (printToResultsBox) {
try {
if (!baos.toString().equals(tempString)) {
tempString = baos.toString();
resultsBox.setText(tempString);
}
} catch (Exception ex) {
}
}
}
}
...
ResultsPrinter rp = new ResultsPrinter();
Thread thread = new Thread(rp);
thread.start();
// Do the translation.
try {
printToResultsBox = true;
boolean success = false;
TranslationEngine te = new TranslationEngine();
// fileOrFolderToConvert is a text box in the GUI.
// linkNeeded and destinationFile are just parameters for the translation process.
success = te.translate(fileOrFolderToConvert.getText(), linkNeeded, destinationFile, printStream);
if (success) {
printStream.println("File/folder translation was a success.");
}
resultsBox.setText(baos.toString());
} catch (Exception ex) {
printStream.println("File translation failed.");
} finally {
printToResultsBox = false;
}
...
}
Ultimately, this code prints out to the JTextPane just fine after all the translation is done but not during. Any suggestions? Do I need to change the PrintStream to something else?
The problem with the way your thread works is you are not on the UI event thread when updating your results box. Take a look at the SwingWorker class. Or you could even use the SwingUtilities.invokeAndWait

Categories

Resources