Tech Stuff - Java

Plenty of resources for Java folks on the web. This page documents a number of problems for which we failed to find (or steal, snaffle, purloin as you choose) a solution on the web and a trivial (X)SSI expansion utility. If this stuff is useful feel free to use (steal, snaffle or purloin as you choose).

Note: We are not Java guys, necessity, as always, however, is the mother of invention. There may be be BQF (better, quicker, faster) and in the case of Java "purer" ways of doing this stuff that we failed to find. If so write us off as the Java neophytes that we willingly admit to being.

Editing HTML Forms

We wanted to be able to intercept editing of certain INPUT tags on HTML FORMs within a JEditorPane and call specialized editors that provide unique formatting for the value(s) being edited immediately the user showed an intention of wishing to edit these fields (right click in field for example). In our case possibly any one of 5 - 10 specialized editors are called, depending on the type of field being edited. These editors typically popup a Modal JDialog and offer content specific editing support, formatting and validation. Some fields we ignore, such as short text editing and SELECT tags and let the default editor handle them. While the processes described reflects our specific requirement they should work for anything with some mods which we note in the code snippet comments where sensible.

When using JEditorPane with HTML Forms (FORM tag) the editing function for INPUT tags with type="text" is handled in a JTextField, type="password" in a JPasswordField (SELECT tags use JComboBox and TEXTAREA uses JTextArea and yet others for check boxes and radio buttons). These editing components are not visible using the object hierarchy obtained from the HTMLDocument interface of JEditorPane. The HTMLDocument hierarchy terminates in a Document object. The Document interface supports documentListeners - all of which are triggered after the edit has been completed. Specifically, the Document interface does not support mouseListeners which would allow notification immediately the user enters a field or double clicks (or however you want to trigger the edit interface).

However, the JTextField, JPasswordField (and JComboBox, JTextArea and others) components do allow multiple listener types and specifically (in our case) mouseListeners. Interestingly, they also terminate in the Document object. Meaning that we can find the final Document by descending the HTMLDocument interface, and if we can find and set appropriate triggers on the JTextField (and others as required) we could get to the same Document.

The only remaining problem is the Document interface has no references back up its hierarchy to the HTMLDocument(for example, a getParent method), so if we reach the Document using the JTextField object we have no idea which INPUT tag it relates to and since it's the INPUT tag which contains interesting things like the name attribute which we want to use to invoke our selective editors.

However, all things are finally possible and the key to tying up the two access routes (via the HTMLDocument and separately via the JTextField/JPasswordField) lies in the Document object's get/putProperty methods. Read on for the gory details.

HTML Form Analysis and Set Triggers

The first step is to find the various INPUT tags in the FORM and track them down to the Document. In the same fragment we show finding the various JTextFields (using an incredibly useful scraper utility) that we want to use as mouseListener triggers. Here's the commented fragment showing what we did:

....
// somewhere on a distant planet we set up the JEditorPane and HTMLEditorKit
// and introduced them to each other
private JEditorPane editor = new JEditorPane();
// Custom HTMLEditorKit (see last snippet)
private SubmitHTMLForm htmlEditor = new SubmitHTMLForm(this, viewer);
editor.setEditorKitForContentType("text/html", htmlEditor);
editor.addHyperlinkListener(hyperlinkListener);
editor.setEditable(false);
editor.setContentType("text/html");
....
// some private/public method that adds a new HTML page
private void setHTMLPageData(String htmlText){
try
{
// htmlText may or may not contain a FORM
// but is a fully formed HTML document
editor.setText(htmlText);
// process the resultant HTML document
HTMLDocument hd = (HTMLDocument)editor.getDocument();
// get a reference to the FORM tag if present starting from the document root
Element elem = hd.getElement(hd.getDefaultRootElement(),
StyleConstants.NameAttribute, HTML.Tag.FORM);
if(elem != null){
// we have a form tag
// now find all the input tags of type=text/password
ElementIterator it = new ElementIterator(elem);
Element input = null;
while((input = it.next()) != null ){
HTML.Tag tag = (HTML.Tag)input.getAttributes().getAttribute(StyleConstants.NameAttribute);
// we are only interested in INPUT tags - add SELECT/TEXTAREA as required
// normally tons of TABLE, TR and TD tags as well cluttering up the layout
if (tag == HTML.Tag.INPUT) {
// get the type HTML attribute
String type = (String)input.getAttributes().getAttribute(HTML.Attribute.TYPE);
// filter out the types we are interested in
if(type.equals("text") || type.equals("password")){
AttributeSet attr = input.getAttributes();
// now get the name= HTML attribute which is the unique data we are interested in
// but could be , say, the value= HTML attribute
String name = (String)attr.getAttribute(HTML.Attribute.NAME);
// oops - no name lets get out of here PDQ
if (name != null) {
// get the Document object for this INPUT tag
Document doc = (Document)attr.getAttribute(StyleConstants.ModelAttribute);
// add name= of INPUT tag to the document - will be retrieved by mouseListener
// property is key and value (both objects - so lots of flexibility)
doc.putProperty(HTMLID, name);
}
}
}
}
// Now re-process the form looking for editing components
// Find all the JTextFields/JPasswordFields in the current HTML document
List<JTextField> tf = SwingUtils.getDescendantsOfType(JTextField.class, editor);
for(JTextField f: tf){
// you could use any event type supported by JTextField
// we wanted a double-click-to-edit interface
f.addMouseListener(this);
}
}
}
catch (Exception e)
{
// yes it can all go horribly wrong
}
}

Note:SwingUtils (written by Darryl Burke - no copyright claimed) is a fantastic 10 line function that will find any class in its container (JEditorPane). In this case we are using the SwingUtils.getDescendantsOfType to find the JTextField (also finds JPasswordField) from the fully rendered HTML document where we found the FORM tag. No other way to find these components (that we could find). Having found the target objects we simply add a mouseListener to them. The trivial mouse handling code is shown next.

Handle the JTextField Triggers

A nothing-special-mouseListener defined, obviously, in the same class as the above fragment:

Form Submission (Submit Button)

Plenty of stuff about this scattered round the web but it's included here for the sake of completeness since the final handling of all the edits above comes only when the user submits the form.

The key to intercepting the Submit button in the HTML FORM is to extend the HTMLFactory of HTMLEditorKit and use this, in turn, to extend FormView which hooks the submitData method. A commented fragment is shown below:

Capturing Current Data from an HTML Form (Programatic Submit)

We wanted to capture the current state of an HTML Form from a JEditorpane control including all current edits. The trigger, in our case, was when the user toggled to a different editing view (we allow both HTML and a Table editing format) at anytime during the editing process and without forcing the user to click the submit button. The trigger conditions are not important - could be any event that you choose, including periodic saves, the principle remains the same. We tried the JEditorpane.getDocument() interface but the attribute values (HTML.Attribute.VALUE) reflect those that were used to set up the HTML page not their current (possibly changed) state.

Conclusion, we needed to programatically force a submit button operation. Turns out this was trivial in the extreme.

Note: It probably goes without saying that you do need to have Submit button on your HTML Form for this solution to work but we'll say it all the same. This technique can be used (and indeed we do use it) in conjunction with the intercepted Submit or not as you choose.

When the HTML Form is rendered by JEditorpane/HTMLEditorkit the HTML <input type="submit" ... is converted into a JButton control. That's the good news. The bad news is that the JEditorpane.getDocument() does not return a reference to the JButton control. It just gives all the HTML tag/attribute thingies. Instead using the excellent SwingUtils (written by Darryl Burke - no copyright claimed) we need to scan the container for all JButton references and save a reference to our target (the Submit in our case though the solution would work for any button). When we need to invoke a submit programmatically we simply use the JButton.doClick() method inherited from the AbstractButton class. Here's the code snippets for your delight and edification:

// HTML Form snippet
<html>
// appropriate head stuff - css, title and so on
....
<body>
// Eye candy or essential HTML
....
// The serious stuff starts ....
<form name="someformname">
// table formatting HTML, editing input tags and other good stuff
....
// input submit button for form
<input type="submit" name="Submit" value="Submit"/>
// other buttons may be added as appropraite to application
....
</form>
</body>
</html>
// Java code snippets
....
// somewhere on a distant planet we set up the JEditorPane and HTMLEditorKit
// and introduce them to each other
private JEditorPane editor = new JEditorPane();
....
private JButton submitBtn = null;
// set an HTML page which contains and HTML form in an appropriate galaxy
try
{
editor.setText(someHTMLText);
....
// use swingUtils to get all JButton references in container
List<JButton> tb = SwingUtils.getDescendantsOfType(JButton.class, editor);
for(JButton f: tb){
// At this point the JButton is generic so the only identifier is text on the button face
if("submit".equalsIgnoreCase(f.getText())){
// save a reference to our target JButton
submitBtn = f;
}
}
}
catch (Exception e)
{
// panic here
}
// trigger condition (eventListener, state change or in-line code as required)
// tests for condition as appropriate
// fire submit
if(submitBtn != null){
submitBtn.doClick();
// if you are using the intercepted Submit Form wheeze then the
// submitData function is immediately called
}
....

That's it. You can fire the button as many times as required, say, as an automated save feature every n minites or once to capture users edited data and save a user prompt or as in our case when we switch from one editing view of the data (in HTML format) to another editing view (a table format). Whatever.

Sending System.out (Std.out) to a Log File

There are times when it is useful to be able to capture all relevant diagnostic information into a log file, either during debug or more especially when trying to analyze site-specific user problems. The application generates plenty of log messages using the standard Java Logger object and it is relativly trivial to capture these to a file using FileHandler. In our case, however, problems can arise using TLS/SSL connections. We can selectively turn on SSL tracing (using the javax.net.debug property) but these (voluminous) messaages are sent to System.out (Std.out) and are not captured by the standard ConsoleHandler from which log-to-file is sourced.

The only way to do this is to hook System.out directly by subclassing OutputStream and feed it directly to the FileHandler. To avoid having System.out messages formatted as if they were log messages we also need to subclass SimpleFormatter and recognize our Standard.out messages. We adopted a very crude technique of using a currently unused LogLevel (Level.CONFIG in our case) for this purpose.

Note: The technique below involves hooking System.out which affects the whole VM. It will get any and all outputs to Std.out from any active program. as will be sen from the code sample below we only do this when we trigger log-to-file and (in our case) SSL tracing is also active. Bottom line. Use this technique with extreme caution.

(X)SSI Expansion Utility

We are addicted to the use of (X)SSI includes. IOHO it both allows us to minimise HTML/CSS development and to maximise our ability to change page format and layout quickly. By changing included files only we immediately propagate changes to all HTML files. We use it for meta data, stylesheets, page headers, page footers, left and right hand menus and navigation bars. Essentially everything except the main content block. Great stuff.

The bad news is what to do with HTML viwers (such as Java) or web servers that do not support (X)SSI. These cases require complete, fully expanded HTML/CSS files. Life is too short to do this manualy for more than one file.

We slapped together (wrote would be an exageration) this trivial (and limited) (X)SSI expansion tool. It processes all .html, .htm and .shtml files in a source directory and for each file:

Expands all Apache <!--#include directives from their relative addresses and includes the contents into the html, htm or shtml file.

Copies all image files (referenced in <img> tags) to a target/images folder.

Changes the image reference in the image tag to point to the new location.

All other Apache (X)SSI directives are left unmodified.

Writes the expanded and/or modified file to a target directory (silently overwriting files with the same name).

The net result being files that will load on any server or HTML viewer. Eureka.

We claim no copyright for the utitlity or its source code. Feel free to use in any way you see fit (subject only to the normal fitness for use/purpose caveats). If you develop it further we'd be very happy if you send us the changes but we do not insist on it. The utility may be dowloaded as a jar (20K) for direct execution. However, if you don't trust us or wish to develop it further then download (20K) the source (single XSSI.java module) and build the jar in your favorite Java IDE having thoroughly checked the source code.

Documentation for this utility consists of a pop-up window when the jar is initially loaded which explains what to do. Want more extensive documentation. Tough.

Problems, comments, suggestions, corrections (including broken links) or something to add? Please take the time from a busy life to 'mail us' (at top of screen), the webmaster (below) or info-support at zytrax. You will have a warm inner glow for the rest of the day.