Java Document to String

This is a very simple utility function to produce an indented XML document as a String given a Document. It throws a TransformerException if anything drastic goes wrong.

JAVA:
  1. import java.io.*;
  2. import org.w3c.dom.Document;
  3. import javax.xml.transform.*;
  4. import javax.xml.transform.dom.DOMSource;
  5.  
  6. ...
  7.  
  8. public static String documentToString( Document document ) throws TransformerException {
  9.     TransformerFactory tFactory = TransformerFactory.newInstance();
  10.     Transformer transformer = tFactory.newTransformer();
  11.    
  12.     DOMSource source = new DOMSource(document);
  13.     StringWriter sw = new StringWriter();
  14.     StreamResult result = new StreamResult(sw);
  15.    
  16.     transformer.setOutputProperty( OutputKeys.INDENT, "yes" );
  17.     transformer.transform(source, result);
  18.    
  19.     return sw.toString();
  20. }

Velocity if statement is type sensitive…

Problem

I discovered that comparisons in Velocity if statements are type-sensitive the hard way.

For example, I am trying to display the 'selected' option in a html <SELECT> object. The list of possible values is $list, each is a simple 'NameValuePair' value object { String name, String value }.

The object I am editing in this form is $obj, and the field I am testing against is $obj.typeId defined as a long.

CODE:
  1. <select name='typeId' value='$!{obj.typeId}'>
  2. #foreach ( $item in $list )
  3.   #if ( $item.value == $obj.typeId )
  4.     <option value='$item.value' selected='true'>$item.name</option>
  5.   #else
  6.     <option value='$item.value'>$item.name</option>
  7.   #end
  8. #end
  9. </select>

However, the if statement on line 3 is actually comparing a String to a long which are of course never going to be equal.

Solution

The easiest work around in this case was to convert the $obj.typeId into a String before the comparison.

CODE:
  1. <select name='typeId' value='$!{obj.typeId}'>
  2. #set ( $typeId = "$!{obj.typeId}" )
  3. #foreach ( $item in $list )
  4.   #if ( $item.value == $typeId )
  5.     <option value='$item.value' selected='true'>$item.name</option>
  6.   #else
  7.     <option value='$item.value'>$item.name</option>
  8.   #end
  9. #end
  10. </select>

Any comments / feedback welcomed ;)

Time Synchronisation With Windows 2000 or XP

If you're running Windows 2000 or XP at home, it's very unlikley that you have a 'Primary Domain Controller' to give you the time of day. ;)

Windows 2000 & XP has a built in "Windows Time" service that is also compatible with public SNTP Time Servers, all you need to do is configure it.

I chose a local SNTP server from the list: http://support.microsoft.com/kb/262680

CODE:
  1. Microsoft Windows 2000 [Version 5.00.2195]
  2. (C) Copyright 1985-2000 Microsoft Corp.
  3.  
  4. C:\>net time /querysntp
  5. This computer is not currently configured to use a specific SNTP server.
  6.  
  7. The command completed successfully.
  8.  
  9.  
  10. C:\>net time /setsntp:ntp0.uk.uu.net
  11. The command completed successfully.
  12.  
  13.  
  14. C:\>net time /querysntp
  15. The current SNTP value is: ntp0.uk.uu.net
  16.  
  17. The command completed successfully.
  18.  
  19.  
  20. C:\>net stop w32time
  21. The Windows Time service is stopping.
  22. The Windows Time service was stopped successfully.
  23.  
  24.  
  25. C:\>net start w32time
  26. The Windows Time service is starting.
  27. The Windows Time service was started successfully.
  28.  
  29.  
  30. C:\>

Done! Immediately after the last line "net start w32time", I noticed my clock jumping forward a few minutes. You can see messages in the "Event Viewer" from W32Time, for example:

CODE:
  1. Event Type: Information
  2. Event Source:   w32time
  3. Event Category: None
  4. Event ID:   593
  5. Date:      26/07/2005
  6. Time:      11:40:05
  7. User:      N/A
  8. Computer:   LOCALHOST
  9. Description: Time service corrected the clock error by 220 seconds

Fedora Core 4 and SELinux paranoia

The default configuration of Fedora Core 4 with SELinux enabled causes problems when you want to use the UserDir feature in Apache Httpd.

Problem:

SELinux is stopping the httpd processes from accessing your home directory.

CODE:
  1. Forbidden
  2.  
  3. You don't have permission to access /~foo/ on this server.
  4. Additionally, a 403 Forbidden error was encountered while trying to use an ErrorDocument to handle the request.
  5. Apache/2.0.54 (Fedora) Server at localhost Port 80

Solution:

From your shell ...

CODE:
  1. [foo@localhost ~]$ chcon -R -t httpd_user_content_t public_html/

If you get the following...

CODE:
  1. [foo@localhost ~]$ chcon -R -t httpd_user_content_t public_html/
  2. chcon: failed to change context of public_html/index.html to root:object_r:httpd_user_content_t: Operation not permitted

It's probably because "index.html" is zero-length, I just placed an empty file in public_html and it failed first time, I removed the file and it went through the rest of it just fine.

Related Links:
http://the.taoofmac.com/space/Fedora
http://fedora.redhat.com/docs/selinux-faq-fc3/index.html#id2825658

Velocity Templates and Newlines

If you're reading this article you've probably encounted the same problem as I have. I was trying to put a newline '\n' character in the value of a variable in a Velocity template.

Attempt 1:

I assumed \n would work...

CODE:
  1. #if ( !$foo )
  2.   #set ( $foo = "There was no foo set in the Context.\nPlease do something about it." )
  3. #end

This produced There was no foo set in the Context.\nPlease do something about it. as the value of $foo.

Attempt 2:

I just thought this was worth a shot :D

CODE:
  1. #if ( !$foo )
  2.   #set ( $foo = "There was no foo set in the Context.
  3. Please do something about it." )
  4. #end

And the template failed to compile

Solution:

Basically, the easiest way I found is to place the newline, carriage return or other special character in the context.

JAVA:
  1. public Template handleRequest( HttpServletRequest request, HttpServletResponse response, Context context )
  2. {
  3.     ...
  4.  
  5.     context.put( "nl", "\n" );
  6.     context.put( "cr", "\r" );
  7.  
  8.     ...
  9. }

And in your template:

CODE:
  1. #if ( !$foo )
  2.   #set ( $foo = "There was no foo set in the Context.${nl}Please do something about it." )
  3. #end

Maybe not the best solution but it works for me. If you expect to use it across your entire application I suggest you override VelocityServlet.createContext in your Servlet to add these characters to every context in your application.

Example:

JAVA:
  1. protected Context createContext( HttpServletRequest request, HttpServletResponse response ) {
  2.     Context context = super.createContext( request, response );
  3.    
  4.     context.put( "nl", "\n" );
  5.     context.put( "cr", "\r" );
  6.    
  7.     return context;
  8. }

Java Object Digester

I was working on a web-based application recently and wanted a lazy way to get data from a simple 'value object' out to the front end without having any hard-coded field names; I came up with this.

Basically, this method will take any Object, iterate through its methods looking for any of the format "get<something>" then place the returned value in a new child element of the document. Obviously the method getClass() will match this criteria and it is handled specially; its added as a comment, not an element.

JAVA:
  1. /**
  2. * <p>Converts a 'value object' into a Document.</p>
  3. *
  4. * <p>This method is expecting a simple value object, it will iterate through a list of
  5. * the objects methods looking for "get*" methods. The remainder of the method name
  6. * is considered the 'field name' ie. getName() - "Name".</p>
  7. *
  8. * <p>The generated document will contain a root element based on the name of the given
  9. * object. Each of its non-null fields will then follow as child elements.</p>
  10. *
  11. * <p>For example, class com.company.value.User which contains methods getName, getEmail and
  12. * getPhoneNumber.</p>
  13. *
  14. * <xmp>
  15. * <user>
  16. *   <name>Michael</name>
  17. *   <email>name@address.com</email>
  18. *   <phonenumber>555-3454</phonenumber>
  19. * </user>
  20. * </xmp>
  21. *
  22. * @author Michael Cutler (m@cotdp.com)
  23. * @param obj the value object to digest into a Document
  24. * @return A document containing the values of all the fields in the object
  25. * @throws ParserConfigurationException thrown if the XML documentbuilder fails
  26. */
  27. public static Document digestObject( Object obj ) throws ParserConfigurationException {
  28.     DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
  29.     DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
  30.    
  31.     Document document = documentBuilder.newDocument();
  32.     Node rootElement  = document.createElement( getClassName(obj) );
  33.    
  34.     document.appendChild( rootElement );
  35.    
  36.     Method methods[] = obj.getClass().getMethods();
  37.     for ( int i = 0; i &lt;methods.length; i++ ) {
  38.         Method method = methods[i];
  39.         if ( method.getName().startsWith("get") ) {
  40.             String fieldName = method.getName().substring(3);
  41.             try {
  42.                 Object result = method.invoke( obj, null );
  43.                 if ( result != null ) {
  44.                     if ( fieldName.equals("Class") ) {
  45.                         Node child = document.createComment( result.toString() + " " );
  46.                         rootElement.appendChild( child );
  47.                     } else {
  48.                         Node child = document.createElement( fieldName );
  49.                         Node text = document.createTextNode( result.toString() );
  50.                         child.appendChild( text );
  51.                         rootElement.appendChild( child );
  52.                     }
  53.                 }
  54.             } catch ( Exception e ) { }
  55.         }
  56.     }
  57.    
  58.     return document;
  59. }
  60.  
  61.  
  62.  
  63.  
  64. /**
  65. * This method is package/no-package safe, lastIndexOf('.') will return -1 if
  66. * not found, since we add 1 to skip the '.' character anyway there is no problem
  67. * if '.' isn't found.
  68. *
  69. * @return a string representing the name of the class (without package name)
  70. */
  71. private static String getClassName( Object obj ) {
  72.     String name = obj.getClass().getName();
  73.     return name.substring( name.lastIndexOf('.') + 1 );
  74. }

For example, if your application stores information about a user - their name, email address & phone number - it may use a simple User value object shown below to represent that information.

JAVA:
  1. package com.company.value;
  2.  
  3. /**
  4. * @author Michael Cutler (m@cotdp.com)
  5. * @version 1.0
  6. */
  7. public class User {
  8.  
  9.     private String name;
  10.     private String email;
  11.     private String phoneNumber;
  12.  
  13.     public String getEmail() {
  14.         return email;
  15.     }
  16.    
  17.     public void setEmail(String email) {
  18.         this.email = email;
  19.     }
  20.    
  21.     public String getName() {
  22.         return name;
  23.     }
  24.    
  25.     public void setName(String name) {
  26.         this.name = name;
  27.     }
  28.    
  29.     public String getPhoneNumber() {
  30.         return phoneNumber;
  31.     }
  32.    
  33.     public void setPhoneNumber(String phoneNumber) {
  34.         this.phoneNumber = phoneNumber;
  35.     }
  36.    
  37. }

Fed into my digestObject() method the resulting Document would look something like:

XML:
  1. <user>
  2.    <name>Michael Cutler</name>
  3.    <email>name@address.com</email>
  4.    <phonenumber>555-3454</phonenumber>
  5. </user>

With your data now in XML you can easily apply XSL transforms on it to render a HTML view or form, use XPath or Schema validation against it to validate the structure of the document and its data or, output it to another system like Velocity for example...

Any comments / questions please post them here, Thanks...

Unicode? Character Sets? UTF-what?

I was inspired to add this note after recent frustrations about the complete ignorance of character sets in both Commercial & Open software...

I seriously recommend everyone reads the following, less ISO-blahblahblah and more UTF-8, no excuses!

The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)
http://www.joelonsoftware.com/articles/Unicode.html