Changeset 25445

Show
Ignore:
Timestamp:
19.04.2012 22:09:50 (8 years ago)
Author:
ak19
Message:

Error reporting is now improved again, much better than in previous commit: no longer just writes out the XSLT stylesheet to the log file, but follows Dr Bainbridge and Sam's suggestion of performing the transformation that failed again with physical files instead of in-memory as before. This points out the exact line location of errors.

Location:
main/trunk/greenstone3/src/java/org/greenstone/gsdl3
Files:
3 modified

Legend:

Unmodified
Added
Removed
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/core/TransformingReceptionist.java

    r25423 r25445  
    593593        Document skinAndLibraryXsl = null; 
    594594        Document skinAndLibraryDoc = converter.newDOM(); 
    595         try 
     595         
     596        // Applying the preprocessing XSLT - in its own block {} to allow use of non-unique variable names 
    596597        { 
    597598 
     
    615616            //In other words, apply the preProcess.xsl to 'skinAndLibraryXsl' in order to 
    616617            //expand all GS-Lib statements into complete XSL statements and also to create 
    617             //a valid  xsl style sheet document. 
    618  
    619             Transformer preProcessor = transformerFactory.newTransformer(new DOMSource(preprocessingXsl)); 
    620             preProcessor.setErrorListener(new XMLTransformer.TransformErrorListener(preprocessingXsl)); 
    621             DOMResult result = new DOMResult(); 
    622             result.setNode(skinAndLibraryDoc); 
    623             preProcessor.transform(new DOMSource(skinAndLibraryXsl), result); 
    624             //System.out.println("GS-Lib statements are now expanded") ;         
    625  
    626         } 
    627         catch (TransformerException e) 
    628         { 
    629             e.printStackTrace(); 
    630             System.out.println("TransformerException while preprocessing the skin xslt"); 
    631             return XMLTransformer.constructErrorXHTMLPage(e.getMessage()); 
    632         } 
    633         catch (Exception e) 
    634         { 
    635             e.printStackTrace(); 
    636             System.out.println("Error while preprocessing the skin xslt"); 
    637             return XMLTransformer.constructErrorXHTMLPage(e.getMessage()); 
    638         } 
     618            //a valid xsl style sheet document. 
     619 
     620            XMLTransformer preProcessor = new XMLTransformer(); 
     621            // Perform the transformation, by passing in: 
     622            // preprocess-stylesheet, source-xsl (skinAndLibraryXsl), and the node that should  
     623            // be in the result (skinAndLibraryDoc) 
     624            preProcessor.transform_withResultNode(preprocessingXsl, skinAndLibraryXsl, skinAndLibraryDoc); 
     625            //System.out.println("GS-Lib statements are now expanded") ; 
     626        } 
     627     
    639628 
    640629        //The following code is to be uncommented if we need to append the extracted GSF statements 
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/GSXML.java

    r25423 r25445  
    12441244    public static String elementToString(Element e, boolean indent) 
    12451245    { 
    1246         String str = "**********START*************\n"; 
    12471246        try { 
    12481247        TransformerFactory tf = TransformerFactory.newInstance(); 
     
    12611260        }  
    12621261        finally { 
    1263         str += "\n***********************\n"; 
    12641262        return str; 
    12651263        } 
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/XMLTransformer.java

    r25423 r25445  
    1919package org.greenstone.gsdl3.util; 
    2020 
     21import org.greenstone.util.GlobalProperties; 
     22 
    2123// XML classes 
    2224import javax.xml.transform.Transformer; 
     
    2830import javax.xml.transform.stream.StreamSource; 
    2931import javax.xml.transform.dom.DOMSource; 
     32import javax.xml.transform.Source; 
    3033import javax.xml.transform.stream.StreamResult; 
    3134import javax.xml.transform.dom.DOMResult; 
     
    4447import java.io.BufferedReader; 
    4548import java.io.FileReader; 
     49import java.io.FileWriter; 
    4650import java.io.File; 
    4751import java.util.HashMap; 
     
    6266 */ 
    6367public class XMLTransformer { 
     68    private static int debugFileCount = 0; // for unique filenames when debugging XML transformations with physical files 
    6469 
    6570    static Logger logger = Logger.getLogger(org.greenstone.gsdl3.util.XMLTransformer.class.getName()); 
     
    118123        // Use the TransformerFactory to process the stylesheet Source and generate a Transformer. 
    119124        Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet)); 
    120         transformer.setErrorListener(new TransformErrorListener(stylesheet)); 
    121125 
    122126        // Use the Transformer to transform an XML Source and send the output to a Result object. 
    123127        StringWriter output = new StringWriter(); 
    124  
    125         transformer.transform(new StreamSource(new StringReader(xml_in)), new StreamResult(output)); 
     128        StreamSource streamSource = new StreamSource(new StringReader(xml_in)); 
     129        transformer.setErrorListener(new TransformErrorListener(stylesheet, streamSource)); 
     130        transformer.transform(streamSource, new StreamResult(output)); 
    126131        return output.toString(); 
    127132    } catch (TransformerConfigurationException e) { 
     
    139144    } 
    140145     
     146 
    141147    public String transformToString(Document stylesheet, Document source, HashMap parameters) { 
    142148     
     
    144150        // Use the TransformerFactory to process the stylesheet Source and generate a Transformer. 
    145151        Transformer transformer = this.t_factory.newTransformer(new DOMSource(stylesheet)); 
    146         transformer.setErrorListener(new TransformErrorListener(stylesheet)); 
    147152        if (parameters != null) { 
    148153        Set params = parameters.entrySet(); 
     
    158163        // Use the Transformer to transform an XML Source and send the output to a Result object. 
    159164        StringWriter output = new StringWriter(); 
    160          
    161         transformer.transform(new DOMSource(source), new StreamResult(output)); 
     165        DOMSource domSource = new DOMSource(source); 
     166 
     167        transformer.setErrorListener(new TransformErrorListener(stylesheet, domSource)); 
     168        transformer.transform(domSource, new StreamResult(output)); 
    162169        return output.toString(); 
    163170    } catch (TransformerConfigurationException e) { 
     
    171178    } 
    172179 
     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 
    173190    public Node transform(Document stylesheet, Document source) { 
    174         return transform(stylesheet, source, null, null); 
     191        return transform(stylesheet, source, null, null, null); 
    175192    } 
    176193     
    177194    public Node transform(Document stylesheet, Document source, HashMap parameters) { 
    178         return transform(stylesheet, source, parameters, null); 
     195        return transform(stylesheet, source, parameters, null, null); 
    179196    } 
    180197 
    181198    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) { 
    182203        try { 
    183204            // Use the TransformerFactory to process the stylesheet Source and generate a Transformer. 
    184205            Transformer transformer = this.t_factory.newTransformer(new DOMSource(stylesheet)); 
    185206            logger.info("XMLTransformer transformer is " + transformer); 
    186             transformer.setErrorListener(new TransformErrorListener(stylesheet)); 
    187207            if (parameters != null) { 
    188208                Set params = parameters.entrySet(); 
     
    200220            // that does not contain any doctype (like we use to do before). 
    201221            DOMResult result = docDocType == null ? new DOMResult() : new DOMResult(docDocType); 
    202             transformer.transform(new DOMSource(source), result); 
     222            if(resultNode != null) { 
     223                result.setNode(resultNode); 
     224            } 
     225            DOMSource domSource = new DOMSource(source); 
     226            transformer.setErrorListener(new TransformErrorListener(stylesheet, domSource)); 
     227            transformer.transform(domSource, result); 
    203228            return result.getNode(); // pass the entire document 
    204229        }  
     
    214239 
    215240    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) { 
    216250    try { 
    217251        Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet)); 
    218         transformer.setErrorListener(new TransformErrorListener(stylesheet)); 
    219         DOMResult result = new DOMResult(); 
    220         transformer.transform(new StreamSource(source), result); 
     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); 
    221258        return result.getNode().getFirstChild(); 
    222259    } catch (TransformerConfigurationException e) { 
     
    230267                + stylesheet + "\n" + source, e); 
    231268    }    
    232  } 
    233   
    234     public Node transform(File stylesheet, File source, Document docDocType) { 
    235         try { 
    236             Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet)); 
    237             transformer.setErrorListener(new TransformErrorListener(stylesheet)); 
    238             DOMResult result = new DOMResult(docDocType); 
    239             transformer.transform(new StreamSource(source), result); 
    240             return result.getNode().getFirstChild(); 
    241         } catch (TransformerConfigurationException e) { 
    242             return transformError("XMLTransformer.transform(File, File, Doc)" 
    243                 + "\ncouldn't create transformer object for files\n"  
    244                 + stylesheet + "\n" + source, e); 
    245         }  
    246         catch (TransformerException e) { 
    247             return transformError("XMLTransformer.transform(File, File, Doc)" 
    248                 + "\ncouldn't transform the source for files\n"  
    249                 + stylesheet + "\n" + source, e); 
    250         }    
    251     } 
    252      
     269    } 
     270      
    253271    // Given a heading string on the sort of transformation error that occurred and the exception object itself,  
    254272    // this method prints the exception to the tomcat window (system.err) and the greenstone log and then returns 
     
    317335    } 
    318336     
    319     // ErrorListener class that can be used to register a handler for any fatal errors, errors and warnings that may 
    320     // occur when transforming an xml file with an xslt stylesheet. The errors are printed both to the greenstone.log and  
    321     // to the tomcat console (System.err), and the error message is stored in the errorMessage variable so that it can 
    322     // be retrieved and be used to generate an xhtml error page. 
    323     static public class TransformErrorListener implements ErrorListener { 
     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 { 
    324343        protected String errorMessage = null; 
    325344        protected String stylesheet = null; 
    326         protected String file = 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 
    327347         
    328         public TransformErrorListener(String xslt) {  
     348        public TransformErrorListener(String xslt, Source source) {  
    329349        this.stylesheet = xslt;  
    330         } 
    331  
    332         public TransformErrorListener(Document xslt) {  
    333         //this.stylesheet = GSXML.xmlNodeToString(xslt);  
     350        this.source = source; 
     351        XMLTransformer.debugFileCount++; 
     352        } 
     353 
     354        public TransformErrorListener(Document xslt, Source source) {        
    334355        this.stylesheet = GSXML.elementToString(xslt.getDocumentElement(), true); 
    335         } 
    336  
    337         public TransformErrorListener(File xslt) {  
    338         stylesheet = "";         
    339         file = xslt.getAbsolutePath(); 
    340         String error = "Can't locate stylesheet file: " + xslt; 
    341  
    342         if(!xslt.exists()) { 
    343             stylesheet = error; 
    344             System.err.println("@@@@@@@ " + error); 
    345             return; 
    346         } 
    347         try { 
    348             BufferedReader in = new BufferedReader(new FileReader(xslt)); 
    349             String line = ""; 
    350             while((line = in.readLine()) != null) { 
    351             stylesheet = stylesheet + line + "\n"; 
    352             } 
    353             in.close(); 
    354             in = null; 
    355         } catch(Exception e) { 
    356             stylesheet = error; 
    357             System.err.println("Exception reading file: " + xslt.getAbsolutePath()); 
    358             e.printStackTrace(); 
    359         } 
     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       
    360365        } 
    361366 
     
    383388        } 
    384389         
    385         // clears the errorPage variable after first call to this method 
     390        // clears the errorPage variable after the first call to this method 
    386391        public String getErrorMessage() { 
    387392        String errMsg = this.errorMessage; 
     
    395400        // and writes the errorMessage to the logger and tomcat's System.err 
    396401        protected void handleError(String errorType, TransformerException exception) { 
    397         this.errorMessage = errorType + toString(exception);  
    398         if(file != null) {  
    399             this.errorMessage = this.errorMessage + "\nfilename: " + file; 
    400         } 
    401         this.errorMessage += "\nException CAUSE:\n" + exception.getCause(); 
    402         System.err.println("\n****Error transforming xml:\n" + this.errorMessage + "\n****\n"); 
    403         System.err.println("Stylesheet was:\n" + this.stylesheet + "\n\n"); 
    404         logger.error(this.errorMessage); 
     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        File styleFile = new File(webLogsTmpFolder + File.separator + "stylesheet" + XMLTransformer.debugFileCount + ".xml"); 
     447        File sourceFile = new File(webLogsTmpFolder + File.separator + "source" + XMLTransformer.debugFileCount + ".xml"); 
     448         
     449        try { 
     450            // write stylesheet to a file called stylesheet_systemID in tmp 
     451            FileWriter styleSheetWriter = new FileWriter(styleFile); 
     452            styleSheetWriter.write(stylesheet, 0, stylesheet.length()); 
     453            styleSheetWriter.flush(); 
     454            styleSheetWriter.close(); 
     455        } catch(Exception e) { 
     456            System.err.println("*** Exception when trying to write out stylesheet to " + styleFile.getAbsolutePath()); 
     457        }        
     458         
     459        try { 
     460            FileWriter srcWriter = new FileWriter(sourceFile); 
     461            String contents = ""; 
     462            if(source instanceof DOMSource) { 
     463            DOMSource domSource = (DOMSource)source; 
     464            Document doc = (Document)domSource.getNode(); 
     465            contents = GSXML.elementToString(doc.getDocumentElement(), true); 
     466            //contents = GSXML.xmlNodeToXMLString(domSource.getNode()); 
     467            } else if (source instanceof StreamSource) { 
     468            StreamSource streamSource = (StreamSource)source; 
     469            BufferedReader reader = new BufferedReader(streamSource.getReader()); 
     470            String line = ""; 
     471            while((line = reader.readLine()) != null) { 
     472                contents = contents + line + "\n"; 
     473            }            
     474            } 
     475            srcWriter.write(contents, 0, contents.length()); 
     476            srcWriter.flush(); 
     477            srcWriter.close();       
     478        } catch(Exception e) { 
     479            System.err.println("*** Exception when trying to write out stylesheet to " + sourceFile.getAbsolutePath()); 
     480        } 
     481         
     482        System.err.println("*****************************************"); 
     483        System.err.println("Look for stylesheet in: " + styleFile.getAbsolutePath()); 
     484        System.err.println("Look for source XML in: " + sourceFile.getAbsolutePath()); 
     485         
     486        // now perform the transform again, which will assign another TransformErrorListener 
     487        // but since debuggingAsFile is turned off, we won't recurse into this section of 
     488        // handling the error again 
     489        XMLTransformer.this.transform(styleFile, sourceFile); // calls the File, File version, so debugAsFile will be false      
     490         
    405491        } 
    406492    }