Basic Question : Swing, ActionListeners, Threads

I've been trying to pick up java again recently after taking a bit of it in college-- I am an experienced programmer but am pretty unfamiliar with java frameworks and the like.

I've constructed a simple webcrawler app that I believe should utilize threading-- the main window basically contains a "start crawling" and a "stop crawling" button.

My current app is modeled after this Java tutorial:

http://java.sun.com/docs/books/tutorial/essential/concurrency/simple.html

However, my app uses a Swing GUI... when the "stop" button is clicked and my actionPerformed() method fires, it knows which button has been clicked, but issuing an interrupt statement to the worker thread seems to have no effect :

t.interrupt();

I moved the declaration of my working thread "t" to the root level of my class, and there are no errors pointed out in my editor or thrown at runtime...

I have tried using isInterrupted() in the worker loop to check if the thread has been interrupted, but it looks like it is just not having an effect on the thread.

Hopefully this is just some oversight on my part... thanks for the help ahead of time.

-Lance

[1189 byte] By [lanceralla] at [2007-11-26 18:03:59]
# 1

> Hopefully this is just some oversight on my part...

If you need further help then you need to create a [url http://homepage1.nifty.com/algafield/sscce.html]Short, Self Contained, Compilable and Executable, Example Program[/url] (SSCCE) that demonstrates the incorrect behaviour, because I can't guess exactly what you are doing based on the information provided.

And don't forget to use the [url http://forum.java.sun.com/help.jspa?sec=formatting]Code Formatting Tags[/url] so the code retains its original formatting.

camickra at 2007-7-9 5:34:15 > top of Java-index,Desktop,Core GUI APIs...
# 2

Sorry, I thought maybe my oversight was conceptual and not necessarily in-code.

forgive the lame "hello world" names, I have yet to fix them. Obviously due to the db interaction you will not be able to try it out yourself, but perhaps this gives you an idea. This is the entire thing, it isn't too terribly long...

notice in the actionPerformed method, t.interrupt and t.join do not seem to get the job done. Similarly, when the run() method in CrawlerThread checks Thread.interrupted, it doesn't seem to get the message.

import java.sql.*;

import java.net.*;

import java.io.*;

import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

public class HelloWorld implements ActionListener {

//private static String labelPrefix = "Number of button clicks: ";

static JLabel queuedLabel = new JLabel("Queued Pages : 0");

static JLabel visitedLabel = new JLabel("Visited Pages : 0");

static JLabel processedLabel = new JLabel("Processed Pages : 0");

static JLabel unProcessedLabel = new JLabel("Unprocessed pages : 0");

static JLabel urlLabel = new JLabel("URL");

static String connectionString = "jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=webcrawler;SelectMethod=Cursor";

/**

* @param args

*/

public static void main(String[] args) throws Exception

{

javax.swing.SwingUtilities.invokeLater(new Runnable()

{

public void run()

{

createAndShowGUI();

getStats();

}

}

);

}

public void actionPerformed(ActionEvent e) {

Thread t = new Thread(new CrawlerThread());

if (e.getActionCommand() == "Crawl")

{

t.start();

}

else if (e.getActionCommand() == "Stop")

{

System.out.println("Stopping..." + Thread.currentThread().getName());

try

{

t.interrupt();

t.join();

}

catch(InterruptedException ie)

{System.out.println("Interrupted Exception : " + ie);}

catch(Exception ex)

{System.out.println("Exception : " + e);}

}

else if (e.getActionCommand() == "Refresh Stats")

{getStats();}

}

public Component createComponents() {

JPanel pane = new JPanel(new GridLayout(2,1));

JPanel statsPanel = new JPanel(new GridLayout(2,1));

JPanel statsSubPanel = new JPanel(new GridLayout(3,1));

JPanel buttonsPanel = new JPanel();

JButton crawlButton = new JButton("Crawl");

crawlButton.addActionListener(this);

JButton statsButton = new JButton("Refresh Stats");

statsButton.addActionListener(this);

JButton stopButton = new JButton("Stop");

stopButton.addActionListener(this);

statsSubPanel.add(queuedLabel);

statsSubPanel.add(visitedLabel);

statsSubPanel.add(processedLabel);

statsSubPanel.add(unProcessedLabel);

statsPanel.add(statsSubPanel);

statsPanel.add(urlLabel);

buttonsPanel.add(crawlButton);

buttonsPanel.add(stopButton);

buttonsPanel.add(statsButton);

pane.add(statsPanel);

pane.add(buttonsPanel);

pane.setBorder(BorderFactory.createEmptyBorder(

30, //top

30, //left

10, //bottom

30) //right

);

return pane;

}

private static void createAndShowGUI() {

//Make sure we have nice window decorations.

//JFrame.setDefaultLookAndFeelDecorated(true);

//Create and set up the window.

JFrame frame = new JFrame("Web Crawler 0.1b");

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

HelloWorld app = new HelloWorld();

Component contents = app.createComponents();

frame.getContentPane().add(contents, BorderLayout.CENTER);

//Display the window.

frame.pack();

frame.resize(500, 200);

frame.setVisible(true);

}

public static void storeRawData(String data, String url)

{

String connectionString;

Connection con;

String insertSQL;

Statement stmt;

connectionString = "jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=webcrawler;SelectMethod=Cursor";

String escapedData = data.replaceAll( "'", "'" + "'" );

insertSQL = "execute sp_insert_raw_data @url = '" + url + "', @data = '" + escapedData + "';";

try

{

Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");

con = DriverManager.getConnection(connectionString,"sa","");

stmt = con.createStatement();

stmt.executeQuery(insertSQL);

stmt.close();

con.close();

}

catch(Exception e)

{

System.out.println("\n\n" + insertSQL + "\n\n");

e.printStackTrace();

}

}

public static String getPageData(String pageAddr) throws Exception

{

String pageData = null;

try

{

// WebSite Address - host

String websiteAddress;

//html file

String file;

//local file

//String localFile;

//get the website address (i.e.host address) from the page address

//using the URL object

URL url = new URL(pageAddr);

websiteAddress = url.getHost();

//get the file

//ex. if the url is http://www.javarefernce.com/index.jsp,

//then /index.jsp is the file

file = url.getFile();

//set the local file name as the requested file

//localFile = file;

//if file does not exist, like if the url is just

//http://www.javareference.com then the file returned is empty

//in this case set the url as file and local file name to index.html

/*if(file.length() == 0)

{

file = pageAddr;

localFile = "index.html";

}*/

//creating a socket to the website using the website address

//and port 80

Socket clientSocket = new Socket(websiteAddress, 80);

System.out.print("Socket opened to " + websiteAddress + "\n");

//creating a BufferReader object using the input stream reader

//this will read the content send by the webserver

BufferedReader inFromServer = new BufferedReader (new InputStreamReader(clientSocket.getInputStream()));

//Need to create a output stream writer

//that will talk to the webserver of the website

OutputStreamWriter outWriter = new OutputStreamWriter(clientSocket.getOutputStream());

//make the GET call to the webserver with the desired url or the file name

//which you intent to get, also mention the protocol type, which is HTTP/1.0

//This call will trigger the webserver to throw this page, which will be read

//by the input stream

//making a get call to the file

outWriter.write("GET " + file + " HTTP/1.0\r\n\n");

outWriter.flush();

//localFile = localFile.substring(localFile.lastIndexOf('/') + 1);

//creating a BufferWriter to create and write into the file locally

//BufferedWriter out = new BufferedWriter(new FileWriter(localFile));

//This loop reads the file

boolean more = true;

String input;

while (more)

{

//read one line at a time

input = inFromServer.readLine();

//print the line if any

if (input == null)

more = false;

else

{

System.out.print("*");

pageData += input;

//out.write(input);

}

}

System.out.println();

//System.out.println("\nPage received successfully..." + "\n");

//out.close();

//close the client socket

clientSocket.close();

}

catch (IOException e)

{

System.out.println ("Error getting page " + e);

}

//System.out.print("Page Contents : " + pageData);

return pageData;

}

public static String getURL()

{

String url;

Connection con;

String selectSQL;

Statement stmt;

ResultSet mySet;

url = null;

mySet = null;

selectSQL = "execute sp_visit_site;";

try

{

Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");

con = DriverManager.getConnection(connectionString,"sa","");

stmt = con.createStatement();

mySet = stmt.executeQuery(selectSQL);

mySet.next();

url = mySet.getString("url");

//String ext = url.substring(url.length()-3-1, url.length());

if (url.endsWith(".com")) {url = url + "/index.html";}

System.out.println("url :" + url);

stmt.close();

con.close();

}

catch(Exception e)

{

e.printStackTrace();

}

return url;

}

public static void getStats()

{

Connection con;

String selectSQL;

Statement stmt;

ResultSet mySet;

mySet = null;

selectSQL = "execute sp_stats;";

try

{

Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");

con = DriverManager.getConnection(connectionString,"sa","");

stmt = con.createStatement();

mySet = stmt.executeQuery(selectSQL);

mySet.next();

queuedLabel.setText("Queued : " + mySet.getString("qCount"));

visitedLabel.setText("Visited : " + mySet.getString("vCount"));

processedLabel.setText("Processed : " + mySet.getString("sCount"));

unProcessedLabel.setText("Unprocessed : " + mySet.getString("rCount"));

stmt.close();

con.close();

}

catch(Exception e)

{

e.printStackTrace();

}

}

private static class CrawlerThread implements Runnable

{

public void run() {

//int count = 0;

String url = null;

String data = null;

boolean first = true;

//while (count < 1)

//{

while (url != null || first == true)

try

{

first = false;

//count++;

Thread.sleep(4000);

if (Thread.interrupted())

{

System.out.println("We've been interrupted...");

return;

}

getStats();

System.out.println("Running..." + Thread.currentThread().getName());

url = getURL();

if (url != null)

{

urlLabel.setText(url);

data = getPageData(url);

}

else

{}

if (data != null && url != null)

{storeRawData(data, url);}

}

catch(InterruptedException ex)

{

System.out.println(ex);

}

catch(Exception e)

{

System.out.println(e);

}

//}

}

}

}

lanceralla at 2007-7-9 5:34:15 > top of Java-index,Desktop,Core GUI APIs...
# 3

> I thought maybe my oversight was conceptual and not necessarily in-code.

Don't know what you mean by conceptual? If you works in the example code you have why wouldn't it work in the GUI.

If you are not sure that it would work in a GUI, then write a simple test, thats what a SSCCE is for.

> Obviously due to the db interaction you will not be able to try it out yourself,

Then why did you include the code. The purpose of a SSCCE is to include only the code that demonstrates the problem. A simple System.out.println(...) would verify your results.

By the way you don't use "==" to compare Objects. You use the equals(...) method.

Anyway it does work on a GUI. Here is my SSCCE to show it:

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

public class InvokeLaterThread extends JFrame

implements ActionListener, Runnable

{

JLabel status;

JButton newThread;

JButton stop;

Thread thread;

int i;

public InvokeLaterThread()

{

status = new JLabel( "Ready to Process:" );

status.setHorizontalAlignment( JLabel.CENTER );

getContentPane().add(status, BorderLayout.NORTH);

newThread = new JButton( "Start Processing" );

newThread.addActionListener( this );

getContentPane().add(newThread, BorderLayout.EAST);

stop = new JButton( "Stop Processing" );

stop.addActionListener( this );

getContentPane().add(stop, BorderLayout.SOUTH);

}

public void actionPerformed(ActionEvent e)

{

if (e.getSource() == newThread)

{

thread = new Thread( this );

thread.start();

}

else

{

try

{

thread.interrupt();

thread.join();

status.setText("Processing Stopped");

newThread.setEnabled( true );

}

catch(InterruptedException ie) {}

}

}

public void run()

{

newThread.setEnabled( false );

try

{

for (i = 1; i < 10; i++)

{

System.out.println("ProcessingFile: " + i);

// SwingUtilities makes sure code is executed in the event thread.

SwingUtilities.invokeLater(new Runnable()

{

public void run()

{

status.setText("Processing File: " + i);

}

});

// simulate log running task

Thread.sleep(1000);

}

}

catch(InterruptedException e)

{

System.out.println("Processing interrupted");

}

SwingUtilities.invokeLater(new Runnable()

{

public void run()

{

status.setText("Finished Processing");

newThread.setEnabled( true );

}

});

}

public static void main(String[] args)

{

JFrame frame = new InvokeLaterThread();

frame.setDefaultCloseOperation( EXIT_ON_CLOSE );

frame.pack();

frame.setLocationRelativeTo( null );

frame.show();

}

}

camickra at 2007-7-9 5:34:15 > top of Java-index,Desktop,Core GUI APIs...
# 4

> "Don't know what you mean by conceptual?"

By conceptual I mean conceptual.

> "If you works in the example code you have why wouldn't it work in the GUI."

Exactly. This is what I was trying to find out.

My point is that the non-gui example worked fine, but when I added the gui, it ceased to work as expected, and I had ran out of solutions to try. I'm not trying to tell you that it isn't possible, I just wasn't understanding what piece of this thing I had broken by adding the GUI-- hence a lack in my conceptual understanding of threading and/or event listeners.

I promise I'm not an idiot, this is all of my 3rd day of reintroducing myself to java. I see that a lot of your other responses are similarly patronizing, and in that case I apologize for bothering you with such cr*p that is unworthy of your consideration.

Anyway, I eventually got it to work, hopefully you enjoyed writing your post.

Not quite sure why you feel the need to add the attitude, I'm just looking for some basic help, man.

lanceralla at 2007-7-9 5:34:15 > top of Java-index,Desktop,Core GUI APIs...
# 5

> , I'm just looking for some basic help, man.

What is your problem?

I did give you help. I stated I didn't understand what you "conceptual" problem was. You verbal description of the problem meant nothing to me and gave me no idea what your code was.

So I showed you how to simplify the problem by writing a SSCCE. I then followed that up with a working SSCCE that worked on a GUI to prove that it was possible.

Did you even read the link of what a SSCCE is? Apparently not or you wouldn't have included all the database stuff in the first place. We are not here to debug your programs. You are supposed ot make the effor to isolate the problem. And that is more easily done by removing unnecessary code.

> I promise I'm not an idiot,

Maybe not but you sure don't know how to treat people who take the time to help you out.

Usually I can read between the lines when someone asks a question without all the necessary information. In your case I couldn't, which is why I asked for more information.

Its good to know that in only a single posting you've managed to become a master at asking questions and at alienating people who make an honest effort to help solve your problem.

Notice how no one else even bothered to help. Hopefully that trend continues in future postings.

camickra at 2007-7-9 5:34:15 > top of Java-index,Desktop,Core GUI APIs...