Changeset 25603

Show
Ignore:
Timestamp:
15.05.2012 18:37:43 (7 years ago)
Author:
sjm84
Message:

1. Sam discoverd that the TransformerFactory? used by XMLConverter could also have XMLConverter's TransformerErrorHandler? attached and that this can help point out the line numbers and errors that occur when a TransformerFactory? fails to create a new instance of a Transformer object. The code has been adjusted to allow a TransformerErrorListener? object to be set on a TransformerFactory?, which on detection of error then writes the XSLT stylesheet used to instantiate a Transformer object out to a file. Thereafter, the errorhandler then tries to use the TransformerFactory? to reinstantiate a Transformer object with the file version of the XSLT which at least appears to show an error that can be understood now. 2. Sadly more whitespace formatting changes introduced by Eclipse and Notepad.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/XMLTransformer.java

    r25602 r25603  
    5858import org.apache.log4j.*; 
    5959 
    60 /** XMLTransformer - utility class for greenstone 
    61  * 
     60/** 
     61 * XMLTransformer - utility class for greenstone 
     62 *  
    6263 * transforms xml using xslt 
    63  * 
     64 *  
    6465 * @author <a href="mailto:kjdon@cs.waikato.ac.nz">Katherine Don</a> 
    6566 * @version $Revision$ 
    6667 */ 
    67 public class XMLTransformer { 
    68     private static int debugFileCount = 0; // for unique filenames when debugging XML transformations with physical files 
    69  
    70     static Logger logger = Logger.getLogger(org.greenstone.gsdl3.util.XMLTransformer.class.getName()); 
    71      
    72   /** The transformer factory we're using */ 
    73     TransformerFactory t_factory=null; 
    74    
    75   /** 
    76    * The no-arguments constructor.  
    77    * 
    78    * Any exceptions thrown are caught internally 
    79    *  
    80    * @see javax.xml.transform.TransformerFactory 
    81    */ 
    82     public XMLTransformer() { 
    83     // http://download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/api/index.html?javax/xml/transform/TransformerFactory.html states that 
    84     // TransformerFactory.newInstance() looks in jar files for a Factory specified in META-INF/services/javax.xml.transform.TransformerFactory,  
    85     // else it will use the "platform default" 
    86     // In this case: xalan.jar's META-INF/services/javax.xml.transform.TransformerFactory contains org.apache.xalan.processor.TransformerFactoryImpl 
    87     // as required. 
    88  
    89     // This means we no longer have to do a System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.processor.TransformerFactoryImpl"); 
    90     // followed by a this.t_factory = org.apache.xalan.processor.TransformerFactoryImpl.newInstance(); 
    91     // The System.setProperty step to force the TransformerFactory implementation that gets used, conflicts with 
    92     // Fedora (visiting the Greenstone server pages breaks the Greenstone-tomcat hosted Fedora pages) as Fedora  
    93     // does not include the xalan.jar and therefore can't then find the xalan TransformerFactory explicitly set. 
    94  
    95     // Gone back to forcing use of xalan transformer, since other jars like crimson.jar, which may be on some 
    96     // classpaths, could be be chosen as the TransformerFactory implementation over xalan. This is what used to 
    97     // give problems before. Instead, have placed copies of the jars that Fedora needs (xalan.jar and serializer.jar  
    98     // and the related xsltc.jar which it may need) into packages/tomcat/lib so that it's on the server's classpath 
    99     // and will be found by Fedora. 
    100  
    101     // make sure we are using the xalan transformer 
    102     System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.processor.TransformerFactoryImpl"); 
    103     try { 
    104         this.t_factory = org.apache.xalan.processor.TransformerFactoryImpl.newInstance(); 
    105         //this.t_factory = TransformerFactory.newInstance(); 
    106     } catch (Exception e) { 
    107         logger.error("exception creating t_factory "+e.getMessage()); 
    108     } 
    109     } 
    110  
    111   
    112      
    113   /** 
    114    * Transform an XML document using a XSLT stylesheet 
    115    *  
    116    * @param stylesheet a filename for an XSLT stylesheet 
    117    * @param xml_in the XML to be transformed 
    118    * @return the transformed XML 
    119    */ 
    120     public String transform(String stylesheet, String xml_in) { 
    121      
    122     try { 
    123         // Use the TransformerFactory to process the stylesheet Source and generate a Transformer. 
    124         Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet)); 
    125  
    126         // Use the Transformer to transform an XML Source and send the output to a Result object. 
    127         StringWriter output = new StringWriter(); 
    128         StreamSource streamSource = new StreamSource(new StringReader(xml_in)); 
    129         transformer.setErrorListener(new TransformErrorListener(stylesheet, streamSource)); 
    130         transformer.transform(streamSource, new StreamResult(output)); 
    131         return output.toString(); 
    132     } catch (TransformerConfigurationException e) { 
    133         logger.error("couldn't create transformer object: "+e.getMessageAndLocation()); 
    134         logger.error(e.getLocationAsString());   
    135         return ""; 
    136     } catch (TransformerException e) { 
    137         logger.error("couldn't transform the source: " + e.getMessageAndLocation()); 
    138         return ""; 
    139     }    
    140     } 
    141  
    142     public String transformToString(Document stylesheet, Document source) { 
    143     return transformToString(stylesheet, source, null); 
    144     } 
    145      
    146  
    147     public String transformToString(Document stylesheet, Document source, HashMap parameters) { 
    148      
    149     try { 
    150         // Use the TransformerFactory to process the stylesheet Source and generate a Transformer. 
    151         Transformer transformer = this.t_factory.newTransformer(new DOMSource(stylesheet)); 
    152         if (parameters != null) { 
    153         Set params = parameters.entrySet(); 
    154         Iterator i = params.iterator(); 
    155         while (i.hasNext()) { 
    156             Map.Entry m = (Map.Entry)i.next(); 
    157             transformer.setParameter((String)m.getKey(), m.getValue()); 
    158         } 
    159         } 
    160         //transformer.setParameter("page_lang", source.getDocumentElement().getAttribute(GSXML.LANG_ATT)); 
    161  
    162          
    163         // Use the Transformer to transform an XML Source and send the output to a Result object. 
    164         StringWriter output = new StringWriter(); 
    165         DOMSource domSource = new DOMSource(source); 
    166  
    167         transformer.setErrorListener(new TransformErrorListener(stylesheet, domSource)); 
    168         transformer.transform(domSource, new StreamResult(output)); 
    169         return output.toString(); 
    170     } catch (TransformerConfigurationException e) { 
    171         logger.error("couldn't create transformer object: "+e.getMessageAndLocation()); 
    172         logger.error(e.getLocationAsString());   
    173         return ""; 
    174     } catch (TransformerException e) { 
    175         logger.error("couldn't transform the source: " + e.getMessageAndLocation()); 
    176         return ""; 
    177     } 
    178     } 
    179  
    180      
    181  
    182     /** 
    183      * Transform an XML document using a XSLT stylesheet,  
    184      * but using a DOMResult whose node should be set to the Document donated by resultNode 
    185      */ 
    186     public Node transform_withResultNode(Document stylesheet, Document source, Document resultNode) { 
    187         return transform(stylesheet, source, null, null, resultNode); 
    188     } 
    189  
    190     public Node transform(Document stylesheet, Document source) { 
    191         return transform(stylesheet, source, null, null, null); 
    192     } 
    193      
    194     public Node transform(Document stylesheet, Document source, HashMap parameters) { 
    195         return transform(stylesheet, source, parameters, null, null); 
    196     } 
    197  
    198     public Node transform(Document stylesheet, Document source, HashMap parameters, Document docDocType) { 
    199     return transform(stylesheet, source, parameters, docDocType, null); 
    200     } 
    201  
    202     protected Node transform(Document stylesheet, Document source, HashMap parameters, Document docDocType, Document resultNode) { 
    203         try { 
     68public class XMLTransformer 
     69{ 
     70    private static int debugFileCount = 0; // for unique filenames when debugging XML transformations with physical files 
     71 
     72    static Logger logger = Logger.getLogger(org.greenstone.gsdl3.util.XMLTransformer.class.getName()); 
     73 
     74    /** The transformer factory we're using */ 
     75    TransformerFactory t_factory = null; 
     76 
     77    /** 
     78     * The no-arguments constructor. 
     79     *  
     80     * Any exceptions thrown are caught internally 
     81     *  
     82     * @see javax.xml.transform.TransformerFactory 
     83     */ 
     84    public XMLTransformer() 
     85    { 
     86        // http://download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/api/index.html?javax/xml/transform/TransformerFactory.html states that 
     87        // TransformerFactory.newInstance() looks in jar files for a Factory specified in META-INF/services/javax.xml.transform.TransformerFactory,  
     88        // else it will use the "platform default" 
     89        // In this case: xalan.jar's META-INF/services/javax.xml.transform.TransformerFactory contains org.apache.xalan.processor.TransformerFactoryImpl 
     90        // as required. 
     91 
     92        // This means we no longer have to do a System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.processor.TransformerFactoryImpl"); 
     93        // followed by a this.t_factory = org.apache.xalan.processor.TransformerFactoryImpl.newInstance(); 
     94        // The System.setProperty step to force the TransformerFactory implementation that gets used, conflicts with 
     95        // Fedora (visiting the Greenstone server pages breaks the Greenstone-tomcat hosted Fedora pages) as Fedora  
     96        // does not include the xalan.jar and therefore can't then find the xalan TransformerFactory explicitly set. 
     97 
     98        // Gone back to forcing use of xalan transformer, since other jars like crimson.jar, which may be on some 
     99        // classpaths, could be be chosen as the TransformerFactory implementation over xalan. This is what used to 
     100        // give problems before. Instead, have placed copies of the jars that Fedora needs (xalan.jar and serializer.jar  
     101        // and the related xsltc.jar which it may need) into packages/tomcat/lib so that it's on the server's classpath 
     102        // and will be found by Fedora. 
     103 
     104        // make sure we are using the xalan transformer 
     105        System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.processor.TransformerFactoryImpl"); 
     106        try 
     107        { 
     108            this.t_factory = org.apache.xalan.processor.TransformerFactoryImpl.newInstance(); 
     109            //this.t_factory = TransformerFactory.newInstance(); 
     110            this.t_factory.setErrorListener(new TransformErrorListener()); // handle errors in the xml Source used to instantiate transformers 
     111        } 
     112        catch (Exception e) 
     113        { 
     114            logger.error("exception creating t_factory " + e.getMessage()); 
     115        } 
     116    } 
     117 
     118    /** 
     119     * Transform an XML document using a XSLT stylesheet 
     120     *  
     121     * @param stylesheet 
     122     *            a filename for an XSLT stylesheet 
     123     * @param xml_in 
     124     *            the XML to be transformed 
     125     * @return the transformed XML 
     126     */ 
     127    public String transform(String stylesheet, String xml_in) 
     128    { 
     129 
     130        try 
     131        { 
     132            TransformErrorListener transformerErrorListener = (TransformErrorListener)this.t_factory.getErrorListener(); 
     133            transformerErrorListener.setStylesheet(stylesheet);          
     134            // Use the TransformerFactory to process the stylesheet Source and generate a Transformer. 
     135            Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet)); 
     136 
     137            // Use the Transformer to transform an XML Source and send the output to a Result object. 
     138            StringWriter output = new StringWriter(); 
     139            StreamSource streamSource = new StreamSource(new StringReader(xml_in)); 
     140            transformer.setErrorListener(new TransformErrorListener(stylesheet, streamSource)); 
     141            transformer.transform(streamSource, new StreamResult(output)); 
     142            return output.toString(); 
     143        } 
     144        catch (TransformerConfigurationException e) 
     145        { 
     146            logger.error("couldn't create transformer object: " + e.getMessageAndLocation()); 
     147            logger.error(e.getLocationAsString()); 
     148            return ""; 
     149        } 
     150        catch (TransformerException e) 
     151        { 
     152            logger.error("couldn't transform the source: " + e.getMessageAndLocation()); 
     153            return ""; 
     154        } 
     155    } 
     156 
     157    public String transformToString(Document stylesheet, Document source) 
     158    { 
     159        return transformToString(stylesheet, source, null); 
     160    } 
     161 
     162    public String transformToString(Document stylesheet, Document source, HashMap parameters) 
     163    { 
     164 
     165        try 
     166        { 
     167            TransformErrorListener transformerErrorListener = (TransformErrorListener)this.t_factory.getErrorListener(); 
     168            transformerErrorListener.setStylesheet(stylesheet); 
    204169            // Use the TransformerFactory to process the stylesheet Source and generate a Transformer. 
    205170            Transformer transformer = this.t_factory.newTransformer(new DOMSource(stylesheet)); 
    206             logger.info("XMLTransformer transformer is " + transformer); 
    207             if (parameters != null) { 
     171            if (parameters != null) 
     172            { 
    208173                Set params = parameters.entrySet(); 
    209174                Iterator i = params.iterator(); 
    210                 while (i.hasNext()) { 
    211                     Map.Entry m = (Map.Entry)i.next(); 
    212                     transformer.setParameter((String)m.getKey(), m.getValue()); 
     175                while (i.hasNext()) 
     176                { 
     177                    Map.Entry m = (Map.Entry) i.next(); 
     178                    transformer.setParameter((String) m.getKey(), m.getValue()); 
    213179                } 
    214180            } 
    215  
     181            //transformer.setParameter("page_lang", source.getDocumentElement().getAttribute(GSXML.LANG_ATT)); 
     182 
     183            // Use the Transformer to transform an XML Source and send the output to a Result object. 
     184            StringWriter output = new StringWriter(); 
     185            DOMSource domSource = new DOMSource(source); 
     186 
     187            transformer.setErrorListener(new TransformErrorListener(stylesheet, domSource)); 
     188            transformer.transform(domSource, new StreamResult(output)); 
     189            return output.toString(); 
     190        } 
     191        catch (TransformerConfigurationException e) 
     192        { 
     193            logger.error("couldn't create transformer object: " + e.getMessageAndLocation()); 
     194            logger.error(e.getLocationAsString()); 
     195            return ""; 
     196        } 
     197        catch (TransformerException e) 
     198        { 
     199            logger.error("couldn't transform the source: " + e.getMessageAndLocation()); 
     200            return ""; 
     201        } 
     202    } 
     203 
     204    /** 
     205     * Transform an XML document using a XSLT stylesheet, but using a DOMResult 
     206     * whose node should be set to the Document donated by resultNode 
     207     */ 
     208    public Node transform_withResultNode(Document stylesheet, Document source, Document resultNode) 
     209    { 
     210        return transform(stylesheet, source, null, null, resultNode); 
     211    } 
     212 
     213    public Node transform(Document stylesheet, Document source) 
     214    { 
     215        return transform(stylesheet, source, null, null, null); 
     216    } 
     217 
     218    public Node transform(Document stylesheet, Document source, HashMap parameters) 
     219    { 
     220        return transform(stylesheet, source, parameters, null, null); 
     221    } 
     222 
     223    public Node transform(Document stylesheet, Document source, HashMap parameters, Document docDocType) 
     224    { 
     225        System.err.println("1"); 
     226        return transform(stylesheet, source, parameters, docDocType, null); 
     227    } 
     228 
     229    protected Node transform(Document stylesheet, Document source, HashMap parameters, Document docDocType, Document resultNode) 
     230    { 
     231        try 
     232        { 
     233            System.err.println("1.1"); 
     234            // Use the TransformerFactory to process the stylesheet Source and generate a Transformer. 
     235            TransformErrorListener transformerErrorListener = (TransformErrorListener)this.t_factory.getErrorListener(); 
     236            transformerErrorListener.setStylesheet(stylesheet); 
     237            Transformer transformer = this.t_factory.newTransformer(new DOMSource(stylesheet));          
     238 
     239            System.err.println("1.2"); 
     240            //logger.info("XMLTransformer transformer is " + transformer); //done in ErrorListener 
     241            if (parameters != null) 
     242            { 
     243                Set params = parameters.entrySet(); 
     244                Iterator i = params.iterator(); 
     245                while (i.hasNext()) 
     246                { 
     247                    Map.Entry m = (Map.Entry) i.next(); 
     248                    transformer.setParameter((String) m.getKey(), m.getValue()); 
     249                } 
     250            } 
     251            System.err.println("1.3"); 
    216252            // When we transform the DOMResult, we need to make sure the result of 
    217253            // the transformation has a DocType. For that to happen, we need to create 
     
    220256            // that does not contain any doctype (like we use to do before). 
    221257            DOMResult result = docDocType == null ? new DOMResult() : new DOMResult(docDocType); 
    222             if(resultNode != null) { 
    223                 result.setNode(resultNode); 
     258            if (resultNode != null) 
     259            { 
     260                result.setNode(resultNode); 
    224261            } 
    225262            DOMSource domSource = new DOMSource(source); 
     263            System.err.println("2"); 
    226264            transformer.setErrorListener(new TransformErrorListener(stylesheet, domSource)); 
     265            System.err.println("4"); 
    227266            transformer.transform(domSource, result); 
     267            System.err.println("5"); 
    228268            return result.getNode(); // pass the entire document 
    229         }  
    230         catch (TransformerConfigurationException e) { 
    231             return transformError("XMLTransformer.transform(Doc, Doc, HashMap, Doc)" 
    232                 + "\ncouldn't create transformer object", e); 
    233         }  
    234         catch (TransformerException e) { 
    235             return transformError("XMLTransformer.transform(Doc, Doc, HashMap, Doc)" 
    236                 + "\ncouldn't transform the source", e); 
    237         } 
    238     } 
    239  
    240     public Node transform(File stylesheet, File source) { 
    241     return transform(stylesheet, source, null); 
    242     } 
    243  
    244     // debugAsFile is only to be set to true when either the stylesheet or source parameters  
    245     // are not objects of type File. The debugAsFile variable is passed into the  
    246     // TransformErrorListener. When set to true, the TransformErrorListener will itself create 
    247     // two files containing the stylesheet and source XML, and try to transform the new source 
    248     // file with the stylesheet file for debugging purposes. 
    249     protected Node transform(File stylesheet, File source, Document docDocType) { 
    250     try { 
    251         Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet)); 
    252         DOMResult result = (docDocType == null) ? new DOMResult() : new DOMResult(docDocType); 
    253         StreamSource streamSource = new StreamSource(source); 
    254  
    255         transformer.setErrorListener(new TransformErrorListener(stylesheet, streamSource)); 
    256  
    257         transformer.transform(streamSource, result); 
    258         return result.getNode().getFirstChild(); 
    259     } catch (TransformerConfigurationException e) { 
    260             return transformError("XMLTransformer.transform(File, File)" 
    261                 + "\ncouldn't create transformer object for files\n"  
    262                 + stylesheet + "\n" + source, e); 
    263     }  
    264     catch (TransformerException e) { 
    265         return transformError("XMLTransformer.transform(File, File)" 
    266                 + "\ncouldn't transform the source for files\n"  
    267                 + stylesheet + "\n" + source, e); 
    268     }    
    269     } 
    270       
     269        } 
     270        catch (TransformerConfigurationException e) 
     271        { 
     272            return transformError("XMLTransformer.transform(Doc, Doc, HashMap, Doc)" + "\ncouldn't create transformer object", e); 
     273        } 
     274        catch (TransformerException e) 
     275        { 
     276            return transformError("XMLTransformer.transform(Doc, Doc, HashMap, Doc)" + "\ncouldn't transform the source", e); 
     277        } 
     278    } 
     279 
     280    public Node transform(File stylesheet, File source) 
     281    { 
     282        return transform(stylesheet, source, null); 
     283    } 
     284 
     285    // debugAsFile is only to be set to true when either the stylesheet or source parameters  
     286    // are not objects of type File. The debugAsFile variable is passed into the  
     287    // TransformErrorListener. When set to true, the TransformErrorListener will itself create 
     288    // two files containing the stylesheet and source XML, and try to transform the new source 
     289    // file with the stylesheet file for debugging purposes. 
     290    protected Node transform(File stylesheet, File source, Document docDocType) 
     291    { 
     292        try 
     293        { 
     294            TransformErrorListener transformerErrorListener = (TransformErrorListener)this.t_factory.getErrorListener(); 
     295            transformerErrorListener.setStylesheet(stylesheet); 
     296            Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet)); 
     297            DOMResult result = (docDocType == null) ? new DOMResult() : new DOMResult(docDocType); 
     298            StreamSource streamSource = new StreamSource(source); 
     299 
     300            transformer.setErrorListener(new TransformErrorListener(stylesheet, streamSource)); 
     301 
     302            transformer.transform(streamSource, result); 
     303            return result.getNode().getFirstChild(); 
     304        } 
     305        catch (TransformerConfigurationException e) 
     306        { 
     307            return transformError("XMLTransformer.transform(File, File)" + "\ncouldn't create transformer object for files\n" + stylesheet + "\n" + source, e); 
     308        } 
     309        catch (TransformerException e) 
     310        { 
     311            return transformError("XMLTransformer.transform(File, File)" + "\ncouldn't transform the source for files\n" + stylesheet + "\n" + source, e); 
     312        } 
     313    } 
     314 
    271315    // Given a heading string on the sort of transformation error that occurred and the exception object itself,  
    272316    // this method prints the exception to the tomcat window (system.err) and the greenstone log and then returns 
    273317    // an xhtml error page that is constructed from it. 
    274     protected Node transformError(String heading, TransformerException e) { 
     318    protected Node transformError(String heading, TransformerException e) 
     319    { 
    275320        String message = heading + "\n" + e.getMessage(); 
    276321        logger.error(heading + ": " + e.getMessage()); 
    277          
    278         String location = e.getLocationAsString();       
    279         if(location != null) { 
     322 
     323        String location = e.getLocationAsString(); 
     324        if (location != null) 
     325        { 
    280326            logger.error(location); 
    281327            message = message + "\n" + location; 
     
    284330        return constructErrorXHTMLPage(message); 
    285331    } 
    286      
     332 
    287333    // Given an error message, splits it into separate lines based on any newlines present and generates an xhtml page 
    288334    // (xml Element)  with paragraphs for each line. This is then returned so that it can be displayed in the browser. 
    289     public static Element constructErrorXHTMLPage(String message) { 
    290         try{ 
     335    public static Element constructErrorXHTMLPage(String message) 
     336    { 
     337        try 
     338        { 
    291339            String[] lines = message.split("\n"); 
    292          
    293             Document xhtmlDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 
    294             // <html></html> 
    295             Node htmlNode = xhtmlDoc.createElement("html"); 
    296             xhtmlDoc.appendChild(htmlNode); 
    297             // <head></head> 
    298             Node headNode = xhtmlDoc.createElement("head"); 
    299             htmlNode.appendChild(headNode); 
    300             // <title></title> 
    301             Node titleNode = xhtmlDoc.createElement("title"); 
    302             headNode.appendChild(titleNode); 
    303             Node titleString = xhtmlDoc.createTextNode("Error occurred"); 
    304             titleNode.appendChild(titleString); 
    305              
    306             // <body></body> 
    307             Node bodyNode = xhtmlDoc.createElement("body"); 
    308             htmlNode.appendChild(bodyNode); 
    309              
    310             // finally put the message in the body 
    311             Node h1Node = xhtmlDoc.createElement("h1"); 
    312             bodyNode.appendChild(h1Node); 
    313             Node headingString = xhtmlDoc.createTextNode("The following error occurred:"); 
    314             h1Node.appendChild(headingString); 
    315              
    316             //Node textNode = xhtmlDoc.createTextNode(message); 
    317             //bodyNode.appendChild(textNode); 
    318              
    319             for (int i = 0; i < lines.length; i++) { 
     340 
     341            Document xhtmlDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 
     342            // <html></html> 
     343            Node htmlNode = xhtmlDoc.createElement("html"); 
     344            xhtmlDoc.appendChild(htmlNode); 
     345            // <head></head> 
     346            Node headNode = xhtmlDoc.createElement("head"); 
     347            htmlNode.appendChild(headNode); 
     348            // <title></title> 
     349            Node titleNode = xhtmlDoc.createElement("title"); 
     350            headNode.appendChild(titleNode); 
     351            Node titleString = xhtmlDoc.createTextNode("Error occurred"); 
     352            titleNode.appendChild(titleString); 
     353 
     354            // <body></body> 
     355            Node bodyNode = xhtmlDoc.createElement("body"); 
     356            htmlNode.appendChild(bodyNode); 
     357 
     358            // finally put the message in the body 
     359            Node h1Node = xhtmlDoc.createElement("h1"); 
     360            bodyNode.appendChild(h1Node); 
     361            Node headingString = xhtmlDoc.createTextNode("The following error occurred:"); 
     362            h1Node.appendChild(headingString); 
     363 
     364            //Node textNode = xhtmlDoc.createTextNode(message); 
     365            //bodyNode.appendChild(textNode); 
     366 
     367            for (int i = 0; i < lines.length; i++) 
     368            { 
    320369                Node pNode = xhtmlDoc.createElement("p"); 
    321370                Node textNode = xhtmlDoc.createTextNode(lines[i]); 
     
    323372                bodyNode.appendChild(pNode); 
    324373            } 
     374 
     375            return xhtmlDoc.getDocumentElement(); 
     376 
     377        } 
     378        catch (Exception e) 
     379        { 
     380            String errmsg = "Exception trying to construct error xhtml page from message: " + message + "\n" + e.getMessage(); 
     381            System.err.println(errmsg); 
     382            logger.error(errmsg); 
     383            return null; 
     384        } 
     385    } 
     386 
     387    // ErrorListener class for both Transformer objects and TransformerFactory objects. 
     388    // This class can be used to register a handler for any fatal errors, errors and warnings that 
     389    // may occur when either transforming an xml file with an xslt stylesheet using the XMLTransformer, 
     390    // or when instantiating a Transformer object using the XMLTransformer's TransformerFactory member var. 
     391    // The latter case occurs when the xml Source used to instantiate a Transformer from a TransformerFactory 
     392    // is invalid in some manner, which results in a null Transformer object. However, as no  
     393    // TransformerConfigurationException or TransformerException are thrown in this case, the errors  
     394    // would have not been noticed until things go wrong later when trying to use the (null) Transformer. 
     395    // 
     396    // The errors caught by this ErrorListener class are printed both to the greenstone.log and to the  
     397    // tomcat console (System.err), and the error message is stored in the errorMessage variable so that  
     398    // it can be retrieved and be used to generate an xhtml error page. 
     399    public class TransformErrorListener implements ErrorListener 
     400    { 
     401        protected String errorMessage = null; 
     402        protected String stylesheet = null; 
     403        protected Source source = null; // can be DOMSource or StreamSource 
     404        protected boolean debugAsFile = true; // true if xslt or source are not real physical files 
     405         
     406        // *********** METHODS TO BE CALLED WHEN SETTING AN ERROR LISTENER ON TRANSFORMERFACTORY OBJECTS 
     407        // The default constructor is only for when setting an ErrorListener on TransformerFactory objects 
     408        public TransformErrorListener() { 
     409            this.stylesheet = null; 
     410            this.source = null; 
     411            XMLTransformer.debugFileCount++; 
     412        } 
     413         
     414        public void setStylesheet(Document xslt)  
     415        { 
     416            this.debugAsFile = true; 
     417            this.stylesheet = GSXML.elementToString(xslt.getDocumentElement(), true); 
     418            this.source = null; 
     419        } 
     420         
     421        public void setStylesheet(String xslt)  
     422        { 
     423            this.debugAsFile = true; 
     424            this.stylesheet = xslt; 
     425            this.source = null; 
     426        } 
     427         
     428        public void setStylesheet(File xslt)  
     429        { 
     430            this.debugAsFile = false; // if this constructor is called, we're dealing with physical files for both xslt and source           
     431            this.stylesheet = xslt.getAbsolutePath(); 
     432            this.source = null; 
     433        } 
     434 
     435        // *********** METHODS TO BE CALLED WHEN SETTING AN ERROR LISTENER ON TRANSFORMERFACTORY OBJECTS 
     436        // When setting an ErrorListener on Transformer object, the ErrorListener takes a Stylesheet xslt and a Source 
     437        public TransformErrorListener(String xslt, Source source) 
     438        { 
     439            this.stylesheet = xslt; 
     440            this.source = source; 
     441            XMLTransformer.debugFileCount++; 
     442        } 
     443 
     444        public TransformErrorListener(Document xslt, Source source) 
     445        { 
     446            this.stylesheet = GSXML.elementToString(xslt.getDocumentElement(), true); 
     447            this.source = source; 
     448            XMLTransformer.debugFileCount++; 
     449        } 
     450 
     451        public TransformErrorListener(File xslt, Source source) 
     452        { 
     453            this.debugAsFile = false; // if this constructor is called, we're dealing with physical files for both xslt and source 
     454            this.source = source; 
     455            this.stylesheet = xslt.getAbsolutePath(); // not necessary to get the string from the file 
     456            // all we were going to do with it *on error* was write it out to a file anyway      
     457        } 
     458 
     459        // *********** METHODS CALLED AUTOMATICALLY ON ERROR 
     460         
     461        //  Receive notification of a recoverable error. 
     462        public void error(TransformerException exception) 
     463        { 
     464            handleError("Error:\n", exception); 
     465        } 
     466 
     467        // Receive notification of a non-recoverable error. 
     468        public void fatalError(TransformerException exception) 
     469        { 
     470            handleError("Fatal Error:\n", exception); 
     471        } 
     472 
     473        // Receive notification of a warning. 
     474        public void warning(TransformerException exception) 
     475        { 
     476            handleError("Warning:\n", exception); 
     477        } 
     478 
     479        public String toString(TransformerException e) 
     480        { 
     481            String msg = "Exception encountered was:\n\t"; 
     482            String location = e.getLocationAsString(); 
     483            if (location != null) 
     484            { 
     485                msg = msg + "Location: " + location + "\n\t"; 
     486            } 
     487 
     488            return msg + "Message: " + e.getMessage(); 
     489        } 
     490 
     491        // clears the errorPage variable after the first call to this method 
     492        public String getErrorMessage() 
     493        { 
     494            String errMsg = this.errorMessage; 
     495            if (this.errorMessage != null) 
     496            { 
     497                this.errorMessage = null; 
     498            } 
     499            return errMsg; 
     500        } 
     501 
     502        // sets the errorMessage member variable to the data stored in the exception 
     503        // and writes the errorMessage to the logger and tomcat's System.err 
     504        protected void handleError(String errorType, TransformerException exception) 
     505        { 
     506 
     507            this.errorMessage = errorType + toString(exception); 
     508 
     509            // If either the stylesheet or the source to be transformed with it were not files, 
     510            // so that the transformation was performed in-memory, then the "location" information 
     511            // during the error handling (if any) wouldn't have been helpful. 
     512            // To allow proper debugging, we write both stylesheet and source out as physical files 
     513            // and perform the same transformation again, so that when a transformation error does  
     514            // occur, the files are not in-memory but can be viewed, and any location information 
     515            // for the error given by the ErrorListener will be sensible (instead of the unhelpful 
     516            // "line#0 column#0 in file://somewhere/dummy.xsl"). 
     517            // Note that if the stylesheet and the source it is to transform were both physical  
     518            // files to start off with, we will not need to perform the same transformation again 
     519            // since the error reporting would have provided accurate locations for those. 
     520            if (debugAsFile) 
     521            { 
     522 
     523                performTransformWithPhysicalFiles(); // will give accurate line numbers 
     524 
     525                // No need to print out the current error message (seen in the Else statement below),  
     526                // as the recursive call to XMLTransformer.transform(File, File, false) in method 
     527                // performTransformWithPhysicalFiles() will do this for us. 
     528            } 
     529            else 
     530            { 
     531                // printing out the error message 
     532                // since !debugAsFile, we are dealing with physical files,  
     533                // variable stylesheet would have stored the filename instead of contents 
     534                this.errorMessage = this.errorMessage + "\nstylesheet filename: " + stylesheet;              
     535                 
     536                this.errorMessage += "\nException CAUSE:\n" + exception.getCause(); 
     537                System.err.println("\n****Error transforming xml:\n" + this.errorMessage + "\n****\n"); 
     538                //System.err.println("Stylesheet was:\n + this.stylesheet + "************END STYLESHEET***********\n\n");            
     539 
     540                logger.error(this.errorMessage); 
     541 
     542                // now print out the source to a file, and run the stylesheet on it using a transform() 
     543                // then any error will be referring to one of these two input files. 
     544            } 
     545        } 
     546         
     547        // This method will redo the transformation that went wrong with *real* files:  
     548        // it writes out the stylesheet and source XML to files first, then performs the transformation 
     549        // to get the actual line location of where things went wrong (instead of "line#0 column#0 in dummy.xsl") 
     550        protected void performTransformWithPhysicalFiles() 
     551        { 
     552            File webLogsTmpFolder = new File(GlobalProperties.getGSDL3Home() + File.separator + "logs" + File.separator + "tmp"); 
     553            if (!webLogsTmpFolder.exists()) 
     554            { 
     555                webLogsTmpFolder.mkdirs(); // create any necessary folders 
     556            } 
     557            File styleFile = new File(webLogsTmpFolder + File.separator + "stylesheet" + XMLTransformer.debugFileCount + ".xml"); 
     558            File sourceFile = new File(webLogsTmpFolder + File.separator + "source" + XMLTransformer.debugFileCount + ".xml"); 
     559                 
     560 
     561            try 
     562            { 
     563                // write stylesheet to a file called stylesheet_systemID in tmp 
     564                FileWriter styleSheetWriter = new FileWriter(styleFile); 
     565                styleSheetWriter.write(stylesheet, 0, stylesheet.length()); 
     566                styleSheetWriter.flush(); 
     567                styleSheetWriter.close(); 
     568            } 
     569            catch (Exception e) 
     570            { 
     571                System.err.println("*** Exception when trying to write out stylesheet to " + styleFile.getAbsolutePath()); 
     572            } 
     573 
     574            if(this.source != null) { // ErrorListener was set on a Transformer object 
     575                try 
     576                { 
     577                    FileWriter srcWriter = new FileWriter(sourceFile); 
     578                    String contents = ""; 
     579                    if (source instanceof DOMSource) 
     580                    { 
     581                        DOMSource domSource = (DOMSource) source; 
     582                        Document doc = (Document) domSource.getNode(); 
     583                        contents = GSXML.elementToString(doc.getDocumentElement(), true); 
     584                        //contents = GSXML.xmlNodeToXMLString(domSource.getNode()); 
     585                    } 
     586                    else if (source instanceof StreamSource) 
     587                    { 
     588                        StreamSource streamSource = (StreamSource) source; 
     589                        BufferedReader reader = new BufferedReader(streamSource.getReader()); 
     590                        String line = ""; 
     591                        while ((line = reader.readLine()) != null) 
     592                        { 
     593                            contents = contents + line + "\n"; 
     594                        } 
     595                    } 
     596                    srcWriter.write(contents, 0, contents.length()); 
     597                    srcWriter.flush(); 
     598                    srcWriter.close(); 
     599                } 
     600                catch (Exception e) 
     601                { 
     602                    System.err.println("*** Exception when trying to write out stylesheet to " + sourceFile.getAbsolutePath()); 
     603                } 
     604            } 
     605 
     606            System.err.println("*****************************************"); 
     607            System.err.println("Look for stylesheet in: " + styleFile.getAbsolutePath()); 
     608            if(this.source != null) { // ErrorListener was set on a Transformer object 
     609                System.err.println("Look for source XML in: " + sourceFile.getAbsolutePath()); 
     610            }    
     611 
     612            // now perform the transform again, which will assign another TransformErrorListener 
     613            // but since debuggingAsFile is turned off, we won't recurse into this section of 
     614            // handling the error again 
     615            if(this.source != null) { // ErrorListener was set on a Transformer object 
     616                XMLTransformer.this.transform(styleFile, sourceFile); // calls the File, File version, so debugAsFile will be false 
     617            }  
    325618             
    326             return xhtmlDoc.getDocumentElement(); 
    327              
    328         }catch(Exception e) { 
    329             String errmsg = "Exception trying to construct error xhtml page from message: " + message 
    330                 + "\n" + e.getMessage(); 
    331             System.err.println(errmsg); 
    332             logger.error(errmsg); 
    333             return null; 
    334         } 
    335     } 
    336      
    337     // ErrorListener class that can be used to register a handler for any fatal errors, errors and warnings 
    338     // that may occur when transforming an xml file with an xslt stylesheet using the XMLTransformer.  
    339         // The errors are printed both to the greenstone.log and to the tomcat console (System.err), and the 
    340     // error message is stored in the errorMessage variable so that it can be retrieved and be used to 
    341     // generate an xhtml error page. 
    342     public class TransformErrorListener implements ErrorListener { 
    343         protected String errorMessage = null; 
    344         protected String stylesheet = null; 
    345         protected Source source = null; // can be DOMSource or StreamSource 
    346         protected boolean debugAsFile = true; // true if xslt or source are not real physical files 
    347          
    348         public TransformErrorListener(String xslt, Source source) {  
    349         this.stylesheet = xslt;  
    350         this.source = source; 
    351         XMLTransformer.debugFileCount++; 
    352         } 
    353  
    354         public TransformErrorListener(Document xslt, Source source) {        
    355         this.stylesheet = GSXML.elementToString(xslt.getDocumentElement(), true); 
    356         this.source = source; 
    357         XMLTransformer.debugFileCount++; 
    358         } 
    359  
    360         public TransformErrorListener(File xslt, Source source) { 
    361         this.debugAsFile = false; // if this constructor is called, we're dealing with physical files for both xslt and source 
    362         this.source = source; 
    363         this.stylesheet = xslt.getAbsolutePath(); // not necessary to get the string from the file 
    364                    // all we were going to do with it *on error* was write it out to a file anyway       
    365         } 
    366  
    367         //  Receive notification of a recoverable error. 
    368         public void error(TransformerException exception) { 
    369         handleError("Error:\n", exception); 
    370         } 
    371         // Receive notification of a non-recoverable error. 
    372         public void fatalError(TransformerException exception) { 
    373         handleError("Fatal Error:\n", exception); 
    374         } 
    375         // Receive notification of a warning. 
    376         public void warning(TransformerException exception) { 
    377         handleError("Warning:\n", exception); 
    378         } 
    379          
    380         public String toString(TransformerException e) { 
    381         String msg = "Exception encountered was:\n\t"; 
    382         String location = e.getLocationAsString(); 
    383         if(location != null) {           
    384             msg = msg + "Location: " + location + "\n\t"; 
    385         }  
    386  
    387         return msg + "Message: " + e.getMessage(); 
    388         } 
    389          
    390         // clears the errorPage variable after the first call to this method 
    391         public String getErrorMessage() { 
    392         String errMsg = this.errorMessage; 
    393         if(this.errorMessage != null) { 
    394             this.errorMessage = null; 
    395         } 
    396         return errMsg; 
    397         } 
    398          
    399         // sets the errorMessage member variable to the data stored in the exception 
    400         // and writes the errorMessage to the logger and tomcat's System.err 
    401         protected void handleError(String errorType, TransformerException exception) { 
    402  
    403         this.errorMessage = errorType + toString(exception); 
    404  
    405         // If either the stylesheet or the source to be transformed with it were not files, 
    406         // so that the transformation was performed in-memory, then the "location" information 
    407         // during the error handling (if any) wouldn't have been helpful. 
    408         // To allow proper debugging, we write both stylesheet and source out as physical files 
    409         // and perform the same transformation again, so that when a transformation error does  
    410         // occur, the files are not in-memory but can be viewed, and any location information 
    411         // for the error given by the ErrorListener will be sensible (instead of the unhelpful 
    412         // "line#0 column#0 in file://somewhere/dummy.xsl"). 
    413         // Note that if the stylesheet and the source it is to transform were both physical  
    414         // files to start off with, we will not need to perform the same transformation again 
    415         // since the error reporting would have provided accurate locations for those. 
    416         if(debugAsFile) { 
    417              
    418             performTransformWithPhysicalFiles(); // will give accurate line numbers 
    419          
    420             // No need to print out the current error message (seen in the Else statement below),  
    421             // as the recursive call to XMLTransformer.transform(File, File, false) in method 
    422             // performTransformWithPhysicalFiles() will do this for us. 
    423         } 
    424         else {  
    425             // printing out the error message 
    426             // since !debugAsFile, we are dealing with physical files,  
    427             // variable stylesheet would have stored the filename instead of contents 
    428             this.errorMessage = this.errorMessage + "\nstylesheet filename: " + stylesheet; 
    429              
    430             this.errorMessage += "\nException CAUSE:\n" + exception.getCause(); 
    431             System.err.println("\n****Error transforming xml:\n" + this.errorMessage + "\n****\n"); 
    432             //System.err.println("Stylesheet was:\n + this.stylesheet + "************END STYLESHEET***********\n\n");            
    433              
    434             logger.error(this.errorMessage); 
    435              
    436             // now print out the source to a file, and run the stylesheet on it using a transform() 
    437             // then any error will be referring to one of these two input files. 
    438         } 
    439         } 
    440  
    441         // This method will redo the transformation that went wrong with *real* files:  
    442         // it writes out the stylesheet and source XML to files first, then performs the transformation 
    443         // to get the actual line location of where things went wrong (instead of "line#0 column#0 in dummy.xsl") 
    444         protected void performTransformWithPhysicalFiles() { 
    445         File webLogsTmpFolder = new File(GlobalProperties.getGSDL3Home() + File.separator + "logs" + File.separator + "tmp"); 
    446         if(!webLogsTmpFolder.exists()) { 
    447             webLogsTmpFolder.mkdirs(); // create any necessary folders 
    448         } 
    449         File styleFile = new File(webLogsTmpFolder + File.separator + "stylesheet" + XMLTransformer.debugFileCount + ".xml"); 
    450         File sourceFile = new File(webLogsTmpFolder + File.separator + "source" + XMLTransformer.debugFileCount + ".xml"); 
    451          
    452         try { 
    453             // write stylesheet to a file called stylesheet_systemID in tmp 
    454             FileWriter styleSheetWriter = new FileWriter(styleFile); 
    455             styleSheetWriter.write(stylesheet, 0, stylesheet.length()); 
    456             styleSheetWriter.flush(); 
    457             styleSheetWriter.close(); 
    458         } catch(Exception e) { 
    459             System.err.println("*** Exception when trying to write out stylesheet to " + styleFile.getAbsolutePath()); 
    460         }        
    461          
    462         try { 
    463             FileWriter srcWriter = new FileWriter(sourceFile); 
    464             String contents = ""; 
    465             if(source instanceof DOMSource) { 
    466             DOMSource domSource = (DOMSource)source; 
    467             Document doc = (Document)domSource.getNode(); 
    468             contents = GSXML.elementToString(doc.getDocumentElement(), true); 
    469             //contents = GSXML.xmlNodeToXMLString(domSource.getNode()); 
    470             } else if (source instanceof StreamSource) { 
    471             StreamSource streamSource = (StreamSource)source; 
    472             BufferedReader reader = new BufferedReader(streamSource.getReader()); 
    473             String line = ""; 
    474             while((line = reader.readLine()) != null) { 
    475                 contents = contents + line + "\n"; 
    476             }            
    477             } 
    478             srcWriter.write(contents, 0, contents.length()); 
    479             srcWriter.flush(); 
    480             srcWriter.close();       
    481         } catch(Exception e) { 
    482             System.err.println("*** Exception when trying to write out stylesheet to " + sourceFile.getAbsolutePath()); 
    483         } 
    484          
    485         System.err.println("*****************************************"); 
    486         System.err.println("Look for stylesheet in: " + styleFile.getAbsolutePath()); 
    487         System.err.println("Look for source XML in: " + sourceFile.getAbsolutePath()); 
    488          
    489         // now perform the transform again, which will assign another TransformErrorListener 
    490         // but since debuggingAsFile is turned off, we won't recurse into this section of 
    491         // handling the error again 
    492         XMLTransformer.this.transform(styleFile, sourceFile); // calls the File, File version, so debugAsFile will be false      
    493          
    494         } 
     619            else { // ErrorListener was set on a TransformerFactory object 
     620                 
     621                // The recursive step in this case is to perform the instantiation  
     622                // of the Transformer object again. 
     623                // Only one TransformerFactory object per XMLTransformer,  
     624                // and only one TransformerHandler object set on any TransformerFactory 
     625                // But the stylesheet used to create a Transformer from that TransformerFactory 
     626                // object changes each time, by calls to setStylesheet(),  
     627                // Therefore, the debugAsFile state for the single TransformerFactory's  
     628                // TransformerHandler changes each time also. 
     629                 
     630                try { 
     631                    debugAsFile = false; 
     632                    this.stylesheet = styleFile.getAbsolutePath();                   
     633                    //TransformErrorListener transformerErrorListener = (TransformErrorListener)XMLTransformer.this.t_factory.getErrorListener(); 
     634                    //transformerErrorListener.setStylesheet(styleFile); 
     635                    Transformer transformer = XMLTransformer.this.t_factory.newTransformer(new StreamSource(styleFile)); 
     636                    if(transformer == null) { 
     637                        String msg = "XMLTransformer transformer is " + transformer; 
     638                        logger.info(msg); 
     639                        System.out.println(msg + "\n****\n"); 
     640                    } 
     641                } catch (TransformerConfigurationException e) 
     642                { 
     643                    String message = "Couldn't create transformer object: " + e.getMessageAndLocation();                     
     644                    logger.error(message); 
     645                    logger.error(e.getLocationAsString()); 
     646                    System.out.println(message); 
     647                } 
     648                catch (TransformerException e) 
     649                { 
     650                    String message = "Couldn't transform the source: " + e.getMessageAndLocation();  
     651                    logger.error(message); 
     652                    System.out.println(message); 
     653                } 
     654            }  
     655 
     656        } 
    495657    } 
    496658}