A Better Way to Load a Parser

Although we now have a successful demonstration of SAX parsing, there is a glaring problem with our code. Let’s take a look again at how we obtain an instance of XMLReader:

try {
        // Instantiate a parser
        XMLReader parser = 
            new SAXParser(  );
            
        // Register the content handler
        parser.setContentHandler(contentHandler);
        
        // Register the error handler
        parser.setErrorHandler(errorHandler);
            
        // Parse the document
        parser.parse(uri);
        
    } catch (IOException e) {
        System.out.println("Error reading URI: " + e.getMessage(  ));
    } catch (SAXException e) {
        System.out.println("Error in parsing: " + e.getMessage(  ));
    }

Do you see anything that rubs you wrong? Let’s look at another line of our code that may give you a hint:

// Import your vendor's XMLReader implementation here
import org.apache.xerces.parsers.SAXParser;

We have to explicitly import our vendor’s XMLReader implementation, and then instantiate that implementation directly. The problem here is not the difficulty of this task, but that we have broken one of Java’s biggest tenets: portability. Our code cannot run or even be compiled on a platform that does not use the Apache Xerces parser. In fact, it is conceivable that an updated version of Xerces might even change the name of the class used here! Our “portable” Java code is no longer very portable.

What is preferred is to request an instance of a class by the name of the implementation class. This allows a simple String parameter to be changed in your source code. Luckily, this facility is available in SAX 2.0. The org.xml.sax.helpers.XMLReaderFactory class provides the method you should be looking for:

/**
 * Attempt to create an XML reader from a class name.
 *
 * <p>Given a class name, this method attempts to load
 * and instantiate the class as an XML reader.</p>
 *
 * @return A new XML reader.
 * @exception org.xml.sax.SAXException If the class cannot be
 *            loaded, instantiated, and cast to XMLReader.
 * @see #createXMLReader(  )
 */
public static XMLReader createXMLReader (String className)
    throws SAXException {
    
    // Implementation
}

We can use this method in our code like this:

try {
        // Instantiate a parser
        XMLReader parser = 
            
            XMLReaderFactory.createXMLReader(
            
               "org.apache.xerces.parsers.SAXParser");
            
        // Register the content handler
        parser.setContentHandler(contentHandler);
        
        // Register the error handler
        parser.setErrorHandler(errorHandler);
            
        // Parse the document
        parser.parse(uri);
        
    } catch (IOException e) {
        System.out.println("Error reading URI: " + e.getMessage(  ));
    } catch (SAXException e) {
        System.out.println("Error in parsing: " + e.getMessage(  ));
    }

This static method takes in the name of the parser class to load and returns an instantiated version of the class, cast to the XMLReader interface (assuming that it actually does implement XMLReader). If any problems occur, they are all handled and then wrapped in a SAXException that is thrown to the calling program. Add in the additional import statement, remove the vendor-specific parser reference, make the changes noted above, and you should be able to recompile your source file:

import java.io.IOException;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
            // This goes away
            // import org.apache.xerces.parsers.SAXParser;

Suddenly you are writing portable code again! To further portability, it makes sense to store the name of the parser class in a properties file. This allows easy loading of the class at runtime, and it means your code can be moved from platform to platform without recompilation based on the parser being used; instead, only the properties file could be changed. Although the code to read properties files is not provided here, take a look at some code that performs this behavior:

try {
        // Instantiate a parser
        XMLReader parser = 
            
            XMLReaderFactory.createXMLReader(
            
               PropertiesReader(  ).getInstance(  )
            
                                 .getProperty("parserClass"));
        
        // Register the content handler
        parser.setContentHandler(contentHandler);
        
        // Register the error handler
        parser.setErrorHandler(errorHandler);
    
        // Parse the document
        parser.parse(uri);
        
    } catch (IOException e) {
        System.out.println("Error reading URI: " + e.getMessage(  ));
    } catch (SAXException e) {
        System.out.println("Error in parsing: " + e.getMessage(  ));
    }

Here a utility class, PropertiesReader, is being used to read a properties file and return the value for the key “parserClass”, which would contain the parser class name to use on the specific platform the code was being used for. In our examples, this would be our old friend org.apache.xerces.parsers.SAXParser. Of course, Java system properties could also be used, but they are not as appropriate for web-centric distributed applications like the ones we focus on in this book, as they must be specified on a command line. Often, distributed applications are started up in whole, rather than individually, making specification of a system property to one particular component difficult.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.20.1