Archive | October, 2006

Java Swing GUI Threads/FoxTrot/SwingWorker/SwingUtilities

16 Oct

Java GUI’s:
(Summarising Ben Galbraith excellent tutorials on javalobby)

Native OS -> AWT Event Queue <- AWT Event Dispatch Thread

All GUI operations have to occur on the Event Dispatch Thread (EDT).
It happens that all EventListeners are also processed on EDT.

This means that if an EventListener has heavy code in it, the EDT slows down
and GUI operations (e.g. redraw screen, processes mouse click) slow down as
they remain in the AWT Event Queue (the EDT is busy processing your heavy code).
This will result in a 'laggy' interface for the user.

(
Generally, all operations modifiying REALIZED Swing components must take place
on the EDT, but there are some exceptions. These methods are Thread-safe:

JComponent.revalidate()
JComponent.repaint()
JComponent.invalidate()
JTextComponent.setText(String)
JTextComponent.replaceSelection(String)

)

Need Threads for a responsive GUI. A few ways to implement this.

0. Non threaded, laggy original:

dirButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String directory = dirText.getText();
final File dirFile = new File(directory);
if (!dirFile.exists()) return;

DefaultListModel dirListModel = new DefaultListModel();
addFilesToModel(dirFile, dirListModel); // THIS IS THE LAGGY LINE
dirList.setModel(dirListModel); // THIS LINE UPDATES THE GUI
}
});

1. Use SwingUtilities for an asynchronous Thread
If you have an EventListener that you want to update the GUI, you have to
have the EDT do the GUI update (the EDT has to do ALL GUI updates!!!).

dirButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String directory = dirText.getText();
final File dirFile = new File(directory);
if (!dirFile.exists()) return;

dirButton.setEnabled(false); // DISABLE TO PREVENT USER CLICKING IT AGAIN!

new Thread() {
public void run() {
final DefaultListModel dirListModel = new DefaultListModel(); // HAS TO BE FINAL AS PASSED TO INNER CLASS
addFilesToModel(dirFile, dirListModel); // THIS IS THE LAGGY LINE
SwingUtilities.invokeLater(new Runnable() {
public void run() {
dirList.setModel(dirListModel); // THIS LINE UPDATES THE GUI ON THE EDT
dirButton.setEnabled(true); // RE-ENABLE BUTTON
}
});
}
}.start();
}
});

2. Use SwingWorker for an asynchronous Thread (not part of JDK)
SwingWorker basically tidys up SwingUtilities, also with a good IDE you can just
refactor this out. Converting this anonymous inner class into an inner class which
can be moved around easily.


dirButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String directory = dirText.getText();
final File dirFile = new File(directory);
if (!dirFile.exists()) return;

dirButton.setEnabled(false); // DISABLE TO PREVENT USER CLICKING IT AGAIN!
SwingWorker swingWorker = new SwingWorker() {
public Object construct() {
// NOTE !!! - THIS DOES NOT HAPPEN ON THE EDT, SO NO GUI OPERATIONS HERE
DefaultListModel dirListModel = new DefaultListModel(); // NO LONGER HAS TO BE FINAL
addFilesToModel(dirFile, dirListModel); // THIS IS THE LAGGY LINE
return dirListModel; // THIS CAN RETURN ANY OBJECT
}

public void finished() {
// NOTE !!! - THIS *DOES* HAPPEN ON THE EDT, SO ALL GUI OPERATIONS HERE -NO LAGGY THINGS
ListModel dirListModel = (ListModel) get(); // GET THE dirListModel RETURNED BY THE 'construct()' METHOD
dirList.setModel(dirListModel); // THIS LINE UPDATES THE GUI ON THE EDT
dirButton.setEnabled(true); // RE-ENABLE BUTTON
}
};
swingWorker.start();
}
});

3. Use FoxTrot for a synchronous Thread
GOOD TO RETROFIT APPLICATIONS WHEN YOU DON’T HAVE PROPER THREADS.

FoxTrot is synchronous, it has access to the Event Queue and can process events in it.
In the following code it looks like the “Worker.post()” call should block and hang waiting
for the “addFilesToModel()” method to return. But in reality FoxTrot is spawning a background
thread to process the commands in its “run()” method. It then monitors the background
thread and waits for it to return, in the meantime it processes anything in the Event
Queue, keeping the GUI responsive. (It can do this because the EDT is the thread that
executes the Listener itself AND FoxTrot’s “post()” method, hence it can access the Event Queue).


dirButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String directory = dirText.getText();
final File dirFile = new File(directory);
if (!dirFile.exists()) return;

dirButton.setEnabled(false); // DISABLE TO PREVENT USER CLICKING IT AGAIN!

ListModel dirListModel = (ListModel) Worker.post(new Job() {
public Object run() {
DefaultListModel dirListModel = new DefaultListModel();
addFilesToModel(dirFile, dirListModel); // THIS IS THE LAGGY LINE
return dirListModel; // THIS CAN RETURN ANY OBJECT
}
});

dirList.setModel(dirListModel); // THIS LINE UPDATES THE GUI ON THE EDT
}
});

Abbot – Java GUI Testing

16 Oct

I was having trouble with launching a program that used the newer (2.7) Xerces library as Abbot itself was using xerces (in lib/).

I tried using “-Djava.endorsed.dirs” on when starting Abbot(i.e. ‘java -Djava.endorsed.dirs=C:\xerces -jar lib/Abbot.jar’) to no avail.

I also tried adding it as a vmarg in ‘Test->Additional VM Args’. I also tried putting my xerces jar at the top of the classpath.

In the end I just replaced the lib/xerces.jar in the Abbot directory with my 2.7.0 xercesImpl.jar (I renamed it), and things work.

The error I was receiving:

061016 15:43:34:483 abbot.script.EventExceptionHandler.exceptionCaught(EventExceptionHandler.java:22):
Unexpected exception while dispatching events:
061016 15:43:34:483 Error at org.jboss.ws.tools.JavaToXSD.parseSchema(JavaToXSD.java:189)
at org.jboss.ws.metadata.wsdl.WSDL11Reader.processTypes(WSDL11Reader.java:227)
at org.jboss.ws.metadata.wsdl.WSDL11Reader.processDefinition(WSDL11Reader.java:118)
...
...
at java.awt.EventDispatchThread.run(Unknown Source): java.lang.NoSuchMethodError: org.apache.xerces.impl.xs.XMLSchemaLoader.loadURIList(Lorg/apache/xerces/xs/StringList;)Lorg/apache/xerces/xs/XSModel;

Java 5 Constants

13 Oct

Java 5 has better support for constants, implemented as an interface

// Create like:
public interface Constants {
public static final String WELCOME = “welcome”;
}


// Use like:
import static Constants.*;
public class test{
public static void main(String[] args) {
System.out.println(WELCOME);
}
}

Bash command prompts

11 Oct

Best prompt:
export PS1=”\[33[36m\][\t]\[33[1;33m\]\u\[33[0m\]@\e[31;1mPRODUCTION:\[33[36m\][\w]:\[33[0m\]”

Favoured stuff:
export PS1=”\[33[36m\][\t]\[33[1;33m\]\u\[33[0m\]@\[\e[32;1m\]\H \e:\[33
[36m\][\w]:\[33[0m\]”
alias vi=”/usr/bin/vim”

Standard prompt:
export PS1="\[33[36m\][\t]\[33[1;33m\]\u\[33[0m\]@\h:\[33[36m\][\w]:\[33[0m\]"

I might prefer these colors:

export PS1="\[\e[36;1m\]\u@\[\e[32;1m\]\H \e[31;1mBACKUP> \[\e[0m\]"

Maybe all green:
export PS1="\[33[36m\][\t]\[33[32;1m\]\u\[33[32;1m\]@\h:\[33[32;1m\][\w]:\[33[0m\] "

Monitoring DRBD using Nagios and SNMP

9 Oct

Wrote a Naguis script to monitor DRBD using SNMP (I don’t really understand why Nagios made up its own plugin system when you can just use SNMP??), Anyway, to make this work you’ll need to:

1. Add a new ‘Check Command’ into Nagios’ checkcommands.cfg, something like: (the check_smpd_drbd.pl script is below)


define command{
command_name check_snmp_drbd
command_line $USER1$/check_snmp_drbd.pl -h $HOSTADDRESS$
}

2. Add a new ‘service’ definition to your services/**.cfg, something like:


define service{
use generic-service
host_name host_to_monitor
service_description DISK STATUS
is_volatile 0
check_period workhours
max_check_attempts 10
normal_check_interval 5
retry_check_interval 2
contact_groups infoservices-admins
notification_interval 120
notification_period 24x7
notification_options c,r
check_command check_snmp_drbd
}

3. On the target machine, you’ll need to make sure your snmpd daemon is sending you what you want, for Net-SNMP i just changed /etc/snmpd/snmpd.conf appending:


exec drbd_data /sbin/drbdadm state data
exec drbd_home /sbin/drbdadm state home
exec drbd_share /sbin/drbdadm state share

4. Make sure your firewall on the target machine allows snmpd through, you might want to put snmpd in your startup scripts to

5. The script to actually do the monitoring from the Nagios machine is:


#!/usr/bin/perl -w
use strict;
use Getopt::Long;

my %ERRORS=('OK'=>0,'WARNING'=>1,'CRITICAL'=>2,'UNKNOWN'=>3,'DEPENDENT'=>4);
my ($hostname, $snmp_resources, $snmp_values);
my (@resources, @values);
my (@tmp, @tmp2, $tmp3);
my ($key, $value);
my %status = ();
my $i;
my $x;
my $error;

# Get the command line options
# only "-h Hostname"
Getopt::Long::Configure ("bundling");
GetOptions(
'h=s' => \$hostname);

# Grab the snmp details - note this should
# probably use Net::SNMP
$snmp_resources=`/usr/bin/snmpwalk -v 1 -c snmponly $hostname 1.3.6.1.4.1.2021.8.1.2 2>/dev/null`;
$snmp_values=`/usr/bin/snmpwalk -v 1 -c snmponly $hostname 1.3.6.1.4.1.2021.8.1.101 2>/dev/null`;

# Didn't get any output - error
if ($snmp_resources eq "" )
{
print "Unknown host: $hostname\n";
exit $ERRORS{"CRITICAL"};
}

@resources = split(/\n/,$snmp_resources);
@values = split(/\n/,$snmp_values);
for ($i=0;$i< $#resources+1;$i++)
{
@tmp = split(/\s+/,$resources[$i]);
@tmp2 = split(/\s+/,$values[$i]);
$tmp3 = $values[$i];
$tmp3 =~ s/UCD-SNMP-MIB::extOutput..* = STRING: //g;
$status{$tmp[3]} = $tmp3;
}

# Check for "Primary/Secondary" or "Secondary/Primary"
while(($key, $value) = each(%status))
{
if (!($value eq "Primary/Secondary") && !($value eq "Secondary/Primary"))
{
print "ERROR: $key says: $value\n";
$error = 1;
}
}

# Send out status
if ($error)
{
exit $ERRORS{"CRITICAL"};
} else {
print "DRBD OK: ";
foreach $key (keys %status)
{
print $key . ",";
}
print "\n";
exit $ERRORS{"OK"};
}

Dynamic JNLP creation using JSP

6 Oct

I needed to create JNLP files dynamically, in Firefox doing this in a JSP file was fine (as long as the mime type was set), but IE didn’t like it – it wanted a link to a .jnlp file.

To fix this, i added the following lines to my WEB-INF/web.xml which maps all calls to a particular jnlp file to a jsp file


 JnlpMapping
 /generate-jnlp.jsp


  JnlpMapping
  /myProgram.jnlp