Implement a J2EE-aware application console in Swing
Tutorial Details:
Implement a J2EE-aware application console in Swing
Implement a J2EE-aware application console in Swing
By: By John Chamberlain
Use JMS to query and control your enterprise application from a Swing console
n essential part of complex enterprise applications is a console. Consoles are the simplest but most flexible user interface. They provide a window that allows the developer or system operator to type a command and receive a text response. Operating systems invariably offer a system console; even the Mac has one, called the Macintosh Programmer's Workshop (MPW).
In an enterprise or application service provider (ASP) environment, the console provides a window into a system's operation and allows operators to configure and control the system in real time. The console can also display unprompted status information and announcements.
In this article, I'll show you how to construct a generic console from Swing components that uses the Java Messaging Service (JMS) to interact with one or more application subsystems. JMS provides a standard solution to the problem of communication between the backend system's command servers and their clients.
Figure 1 shows the console client's on-screen appearance. The user types commands in the interface's lower box ( JTextField ) and receives an answer in the text area above it. The dot before the command indicates that the command goes to the console itself rather than to the connected host or queue. When text fills the text area, scroll bars automatically appear.
Figure 1. The console in action with the default Metal look and feel
Rather than create a distributed communication system in one step, we will build up to the finished product in stages. First, we will construct a basic console that operates synchronously using a socket. This introduces the Swing fundamentals and demonstrates how to build a functional console without the extra JMS complexities. Then we will extend our basic console with useful features, such as tabbed panes. Finally, we will add JMS for industrial-strength communications.
Before getting started
The biggest hurdle to starting with Swing is understanding its underlying object model. Your application or applet must sit above a complex web of classes with subtle and hidden interactions. Later in this article, I will cover some other important topics, such as event threads and peers. To get started, you just need to know the component model. Mastering this foundation takes time, but the inheritance outline below may give you a leg up on the learning curve:
Swing's inheritance model
java.awt.Component (abstract)
java.awt.Container
javax.swing.JComponent (abstract)
javax.swing.JRootPane
...other concrete components (JPanel, JTree etc.)
java.awt.Window
java.awt.Panel
java.applet.Applet
javax.swing.JApplet
javax.swing.JWindow
java.awt.Dialog
javax.swing.JDialog
java.awt.Frame
javax.swing.JFrame
The top-level Swing components ( JApplet , JWindow , JDialog , and JFrame ) are in bold above. Each of these containers has only one component, JRootPane . The diagram below illustrates the JRootPane -- the key to Swing.
Figure 2. The organization of Swing's JRootPane
The root pane has a glass pane on top and a layered pane underneath. The layered pane is composed of a MenuBar and a ContentPane . You add subcomponents to your outer container via the content pane. When you use a component's getContentPane() method, you are actually getting its root pane's content pane.
Because the GlassPane component is always painted last, it appears on top of the ContentPane and MenuBar . The GlassPane lets you shield the underlying layered pane from mouse clicks. You can also use it for graphical overlays. For example, you can move special cursors or animation sprites (images moved on a stationary background) around the glass pane without disturbing the main content.
The console's design is Model-View-Controller
When writing applications with a visual interface, such as a console, you should structure them with the Model-View-Controller (MVC) design in mind (see Design Patterns for a discussion of this architecture). Our example console has three core classes: ApplicationController (the controller), ConsoleInterface (the view), and ConsoleConnection (the model). Strict adherence to the design pattern is not necessary, but you should segregate the application into logical parts that you can write and maintain separately from one another. Many textbook examples of similar applications make the mistake of stuffing everything into one class that implements Runnable . They do this to avoid the tricky problem of passing information between threads, but in the process they teach the wrong approach to Swing. I will show how you can solve this problem and preserve a good design as we build the example console.
The command servers -- the server-side components that listen and respond to console connections -- are an important part of the overall design. CommandServer.java , an example class found in this article's source code , implements a typical command server. The command server listens on a server socket for connections and then acts as an intermediary between the consoles and the server-side components. When using a synchronous server in this way, the command server must act like a messaging router in case the consoles want to access multiple information sources.
Set up the top-level component
Out of the four possible top-level components -- JApplet , JWindow , JDialog , and JFrame -- the JFrame is the best and most full-featured component for creating typical application windows. JWindow has no title bar so it is most commonly used for splash screens, progress bars, and other plain boxes. To create the top-level element, therefore, we first make our interface class, ConsoleInterface , extend JFrame . Listing 1 shows this class in its entirety:
Listing 1. ConsoleInterface.java
/* The interface class plays the role of the view in the
* basic console's Model-View-Controller architecture.
* The processEvent() method overrides processEvent in the
* JFrame's parent container to provide a way for input to
* reach the interface.
*/
package basicconsole;
import java.io.InputStream;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class ConsoleInterface extends JFrame {
private final static int iDEFAULT_FontSize = 12;
ConsoleInterface() {}
private final JPanel panelConsole = new JPanel();
private final JTextArea jtaDisplay = new JTextArea();
private final JTextField jtfCommand = new JTextField(32);
private final JScrollPane jspDisplay = new JScrollPane();
boolean zInitialize(String sTitle, StringBuffer sbError){
try{
// create icon
try {
byte[] abIcon;
InputStream inputstreamIcon =
this.getClass().getResourceAsStream("icon.gif");
int iIconSize = inputstreamIcon.available();
abIcon = new byte[iIconSize];
inputstreamIcon.read(abIcon);
this.setIconImage(new ImageIcon(abIcon).getImage());
} catch(Exception ex) {
// the default icon will be used
}
this.setTitle(sTitle);
// set up content pane
Container content = this.getContentPane();
content.setLayout(new BorderLayout());
panelConsole.setLayout(new BorderLayout());
content.add(panelConsole);
// set up display area
jtaDisplay.setEditable(false);
jtaDisplay.setLineWrap(true);
jtaDisplay.setMargin(new Insets(5, 5, 5, 5));
jtaDisplay.setFont(
new Font("Monospaced", Font.PLAIN, iDEFAULT_FontSize));
jspDisplay.setViewportView(jtaDisplay);
panelConsole.add(jspDisplay, BorderLayout.CENTER);
panelConsole.add(jtfCommand, BorderLayout.SOUTH);
// listener: window closer
this.addWindowListener(
new WindowAdapter(){
public void windowClosing(WindowEvent e){
ApplicationController.getInstance().vExit();}
});
// listener: command box
jtfCommand.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e){
String sCommandAnswer =
ApplicationController.getInstance().sCommand(
jtfCommand.getText());
vDisplayAppendLine(sCommandAnswer);
jtfCommand.setText("");
}
});
this.vResize();
return true;
} catch (Exception ex){
sbError.append(
"Error initializing control panel: " + ex);
return false;
}
}
void vResize(){
Dimension dimScreenSize =
Toolkit.getDefaultToolkit().getScreenSize();
this.setSize(dimScreenSize);
}
void vDisplayAppendLine(String sTextToAppend){
jtaDisplay.append(sTextToAppend + "\r\n");
try { // scroll to end of display
jtaDisplay.scrollRectToVisible(
new Rectangle(0,
jtaDisplay.getLineEndOffset(
jtaDisplay.getLineCount()), 1,1));
} catch(Exception ex) {}
}
protected void vSetFocus() {
jtfCommand.requestFocus();
}
protected void processEvent (AWTEvent e) {
if (e instanceof MessageEvent ) {
vDisplayAppendLine(((MessageEvent)e).getMessage());
return;
}
super.processEvent(e);
}
}
To see the source for the other two classes, ApplicationController and ConsoleConnection , you must download the source code . As soon as the main thread news the ConsoleInterface class ( newing creates an instance of an object; the term derives from the Java keyword new ), our interface will come into existence (but it won't appear on-screen because it is not yet visible). The strategy for bootstrapping the interface from the main thread is:
Create a new ConsoleInterface (the JFrame )
Initialize ConsoleInterface via a method used for that purpose
Invoke ConsoleInterface.visible(true) to make it appear
The initialization method returns true or false depending on whether the interface initialization succeeds. This allows the main thread to recover if for some reason the JFrame could not initialize. The snippet below illustrates how the main thread activates the interface:
if (mInterface.zInitialize(DEFAULT_TITLE, sbError)) {
mInterface.setVisible(true);
} else {
javax.swing.JOptionPane.showMessageDialog(null,
"Failed to initialize interface: " + sbError);
}
If the inter
Read
Tutorial at: Click here to view the tutorial
Rate Tutorial: Implement a J2EE-aware application console in Swing
View Tutorial: Implement a J2EE-aware application console in Swing
Related
Tutorials:
|