You are viewing a plain text version of this content. The canonical link for it is here.
Posted to j-users@xalan.apache.org by Ben Godfrey <be...@hypothetical.co.uk> on 2002/07/02 06:17:30 UTC

Reporting Errors from Extensions

Hi,

I'm trying to implement a set of extensions to Xalan-J 2 that 
provide basic file manipulations (copy, move etc). I am 
developing a system that automatically creates trivial 
brochureware sites from an XML sitemap and some associated files.

Xalan's extension mechanism is fantastic, really easy to use.

I'm hoping that people will be able to use my extensions with 
the org.apache.xalan.xslt.Process command-line interface without 
having to code their own transformer, but I can't find much info 
on the best way to push errors back from my extension functions 
to the command line. At the moment I'm trying to do it by having 
my functions throw TransformerExceptions. This produces 
something like:

file:///Users/ben/Desktop/WEBWAX/map.xsl; Line 45; Column 79; 
java.lang.reflect.InvocationTargetException

Which is pretty good considering I've only been coding for a 
couple of hours. However, I'd really like to throw more 
meaningful error messages at the user.

Just in case it's useful, I've attached the source for my 
extension class so far (I know it's far from finished).

Sorry if I'm missing some obvious piece of documentation and 
thanks in advance for any help anyone can provide. Ideally I'd 
quite like to get this to a state where I can submit it to the 
extension library.

Ben Godfrey

------------- FileSystem.java
/**
  *
  *	Implementation of a set of Xalan extensions for doing basic 
file operations.
  *
  *	Elements:
  *
  *	mkdir
  *	copy
  *	move
  *
  *	XPath functions:
  *
  *	strip-path()
  *	strip-filename()
  *
  */

package org.aftnn.xalan;

// vitally important Java bizniss
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

// Xalan extension interface classes
import org.apache.xalan.extensions.XSLProcessorContext;
import org.apache.xalan.templates.ElemExtensionCall;
import javax.xml.transform.TransformerException;

public class FileSystem {
	//
	//	Elements
	//
	
	public void mkdir(XSLProcessorContext context, 
ElemExtensionCall call) throws 
javax.xml.transform.TransformerException {
		String path = call.getAttribute("path", 
context.getContextNode(), context.getTransformer());
		
		if (path.equals("")) throw new 
TransformerException("Mkdir failed: Please add a path 
attribute.");
		else {
			File f = new File(path);
			if (!f.exists()) f.mkdirs();
		}
	}

	public void copy(XSLProcessorContext context, 
ElemExtensionCall call) throws java.io.IOException, 
javax.xml.transform.TransformerException {
		String src = call.getAttribute("src", 
context.getContextNode(), context.getTransformer());
		String dest = call.getAttribute("dest", 
context.getContextNode(), context.getTransformer());
		String overwrite = call.getAttribute("overwrite", 
context.getContextNode(), context.getTransformer());

		if (src.equals("") || dest.equals("")) throw new 
TransformerException("Copy failed: Please supply both src and 
dest attributes.");
		else {
			File f = new File(src);
			File g = new File(dest);
			File h = new File(g.getPath());

			if (!f.exists()) throw new 
TransformerException("Copy failed: File \"" + src + "\" does not 
exist.");
			else if (!f.canRead()) throw new 
TransformerException("Copy failed: File \"" + src + "\" is not 
readable.");
			else if (!h.isDirectory()) throw new 
TransformerException("Copy failed: \"" + g.getPath() + "\" is 
not a directory.");
			else if (!h.canWrite()) throw new 
TransformerException("Copy failed: Unable to write to \"" + 
g.getPath() + "\".");
			else if (!overwrite.equals("yes") && !g.exists()) 
throw new TransformerException("Copy failed: File \"" + 
dest + "\" already exists and overwrite not set.");
			else {
				int c;
				FileInputStream in = new FileInputStream(f);
				FileOutputStream out = new FileOutputStream(g);
			
				while ((c = in.read()) != -1) out.write(c);
				
				in.close();
				out.close();
			}
		}
	}

	public void move(XSLProcessorContext context, 
ElemExtensionCall call) throws 
javax.xml.transform.TransformerException {
		String src = call.getAttribute("src", 
context.getContextNode(), context.getTransformer());
		String dest = call.getAttribute("dest", 
context.getContextNode(), context.getTransformer());

		if (src.equals("") || dest.equals("")) throw new 
TransformerException("Move failed: Please supply both src and 
dest attributes.");
		else {
			File f = new File(src);
			File g = new File(dest);
			File h = new File(g.getPath());

			if (!f.exists()) throw new 
TransformerException("Move failed: File \"" + src + "\" does not 
exist.");
			else if (!f.canRead()) throw new 
TransformerException("Move failed: File \"" + src + "\" is not 
readable.");
			else if (!h.isDirectory()) throw new 
TransformerException("Move failed: \"" + g.getPath() + "\" is 
not a directory.");
			else if (!h.canWrite()) throw new 
TransformerException("Move failed: Unable to write to \"" + 
g.getPath() + "\".");
			else f.renameTo(g);
		}				
	}

	//
	//	Functions
	//

	public String stripPath(String filespec) {
		File f = new File(filespec);
		return f.getName();
	}

	public String stripFilename(String filespec) {
		File f = new File(filespec);
		return f.getPath();
	}
}
-------------


Re: Reporting Errors from Extensions [and xalan functional-programming]

Posted by "Frank E. Weiss" <fr...@well.com>.
> Ben, as an alternative approach which does not depend on introducing
> side-effects into xslt processing, perhaps you could instead write a
> stylesheet which transforms your input document into a scripting
> language, then execute that scripting language? Perhaps jelly (an
> xml-based scripting language being developed in the apache commons
> project) would be an appropriate scripting language. I don't know if
> jelly already has mkdir/copy/move tags, but I suspect it does, or would
> be happy to accept implementations of them.
>

I'd also suggest using Ant (jakarta.apache.org/ant) as the execution engine. Since Ant's build files are XML, it's very
easy to create them with Xalan. Plus Ant has the style task to run Xalan. A further bonus is that the entire process can
be run in one JVM, avoiding process switching. Example code follows.

<project default="">
 <target name="foils">
  <style
   style="build.xsl"
   in="mapframe.xml"
   out="build/build-swf.xml"
  >
  </style>
  <ant antfile="build/build-swf.xml"/>
 </target>
</project>



RE: Reporting Errors from Extensions [and xalan functional-programming]

Posted by Elliotte Rusty Harold <el...@metalab.unc.edu>.
There's really no good way to do it. Here's what I wrote on that 
subject in Chapter 17 of Processing XML with Java 
<http://www.cafeconleche.org/books/xmljava/chapters/ch17s03.html#d0e33244>:


If the extension function throws an exception, as calculate() might 
if it's passed a negative number as an argument, then the XSLT 
processing will halt. XSLT has no way to catch and respond to 
exceptions thrown by extension functions. Consequently, if you want 
to handle them, you'll need to handle them in the Java code. After 
catching the exception, you'll want to return something. 
Possibilities include:

* A String containing an error message
* A NodeList containing a fault document
* An integer error code

Since this may not be the same type you normally return, you'll 
probably need to declare that the method returns Object to give you 
the additional flexibility. For example, this method returns an error 
message inside a String instead of throwing an exception:

   public static Object calculate(int n) {

     if (n <= 0) {
      return
       "Fibonacci numbers are only defined for positive integers";
     }
     BigInteger low  = BigInteger.ONE;
     BigInteger high = BigInteger.ONE;
    
     for (int i = 3; i <= n; i++) {
       BigInteger temp = high;
       high = high.add(low);
       low = temp;
     }
    
     return high;

   }

This method returns -1 (an illegal value for a Fibonacci number) 
instead of throwing an exception:

   public static BigInteger calculate(int n) {

     if (n <= 0) return new BigInteger("-1");
     BigInteger low  = BigInteger.ONE;
     BigInteger high = BigInteger.ONE;
    
     for (int i = 3; i <= n; i++) {
       BigInteger temp = high;
       high = high.add(low);
       low = temp;
     }
    
     return high;

   }

It would be up to the stylesheet to check for the error code before 
using the result, and handle such a situation appropriately. In this 
example, that might require calling the extension function before any 
output is generated, storing the result in a variable, and deciding 
whether to output a successful response or a fault document based on 
the value of that variable. Waiting until the template for the int 
element is activated would be too late because by that point 
substantial parts of a successful response document have already been 
generated.
-- 

+-----------------------+------------------------+-------------------+
| Elliotte Rusty Harold | elharo@metalab.unc.edu | Writer/Programmer |
+-----------------------+------------------------+-------------------+
|          XML in a  Nutshell, 2nd Edition (O'Reilly, 2002)          |
|              http://www.cafeconleche.org/books/xian2/              |
|  http://www.amazon.com/exec/obidos/ISBN%3D0596002920/cafeaulaitA/  |
+----------------------------------+---------------------------------+
|  Read Cafe au Lait for Java News:  http://www.cafeaulait.org/      |
|  Read Cafe con Leche for XML News: http://www.cafeconleche.org/    |
+----------------------------------+---------------------------------+

RE: Reporting Errors from Extensions [and xalan functional-programming]

Posted by Simon Kitching <si...@ecnetwork.co.nz>.
Hi Ben,

I would also be very interested in any info about reporting errors from
extension functions/elements. Having scanned through docs & code I am
not much wiser about what the best way to report errors are.

Regarding your extensions, though, I am not so sure I like them [NB: I
am not a Xalan developer, just a user]. One of the major features of
XSLT is supposed to be that it is a functional language, ie that
operations do not have side-effects; your mkdir/copy/move extensions are
hardly that :-)

On the other hand, the Xalan project has accepted the SQL extensions,
which are also based around "side-effects".

So may be this is a good chance to ask you Xalan guys what your view on
the functional aspects of xslt are? Do you think that the functional
aspects of xslt are unimportant? Strangely enough, the words
"functional" or "side-effect" don't occur in the specification (I just
did a search), but it is clear that the authors intended this to be the
case. The book "XSLT" (Wrox press) by Michael Kay (author of the saxon
xslt processor) discusses the functional features of xslt at length.

Examples of some of the things that are possible only when xslt is
side-effect-free are:
* handing off processing of different parts of the document to multiple
threads (functional languages are well known for their potential
parallelisation advantages).
* updating a stylesheet "live" based on changes to the underlying DOM.

Maybe you already had this discussion before accepting the SQL
extension? I should check the archives...

Ben, as an alternative approach which does not depend on introducing
side-effects into xslt processing, perhaps you could instead write a
stylesheet which transforms your input document into a scripting
language, then execute that scripting language? Perhaps jelly (an
xml-based scripting language being developed in the apache commons
project) would be an appropriate scripting language. I don't know if
jelly already has mkdir/copy/move tags, but I suspect it does, or would
be happy to accept implementations of them.

Regards,

Simon


> -----Original Message-----
> From: Ben Godfrey [mailto:ben@hypothetical.co.uk] 
> Sent: Tuesday, July 02, 2002 4:18 PM
> To: xalan-j-users@xml.apache.org
> Subject: Reporting Errors from Extensions
> 
> 
> 
> Hi,
> 
> I'm trying to implement a set of extensions to Xalan-J 2 that 
> provide basic file manipulations (copy, move etc). I am 
> developing a system that automatically creates trivial 
> brochureware sites from an XML sitemap and some associated files.
> 
> Xalan's extension mechanism is fantastic, really easy to use.
> 
> I'm hoping that people will be able to use my extensions with 
> the org.apache.xalan.xslt.Process command-line interface without 
> having to code their own transformer, but I can't find much info 
> on the best way to push errors back from my extension functions 
> to the command line. At the moment I'm trying to do it by having 
> my functions throw TransformerExceptions. This produces 
> something like:
> 
> file:///Users/ben/Desktop/WEBWAX/map.xsl; Line 45; Column 79; 
> java.lang.reflect.InvocationTargetException
> 
> Which is pretty good considering I've only been coding for a 
> couple of hours. However, I'd really like to throw more 
> meaningful error messages at the user.
> 
> Just in case it's useful, I've attached the source for my 
> extension class so far (I know it's far from finished).
> 
> Sorry if I'm missing some obvious piece of documentation and 
> thanks in advance for any help anyone can provide. Ideally I'd 
> quite like to get this to a state where I can submit it to the 
> extension library.
> 
> Ben Godfrey
> 
> ------------- FileSystem.java
> /**
>   *
>   *	Implementation of a set of Xalan extensions for doing basic 
> file operations.
>   *
>   *	Elements:
>   *
>   *	mkdir
>   *	copy
>   *	move
>   *
>   *	XPath functions:
>   *
>   *	strip-path()
>   *	strip-filename()
>   *
>   */
> 
> package org.aftnn.xalan;
> 
> // vitally important Java bizniss
> import java.io.File;
> import java.io.FileInputStream;
> import java.io.FileOutputStream;
> 
> // Xalan extension interface classes
> import org.apache.xalan.extensions.XSLProcessorContext;
> import org.apache.xalan.templates.ElemExtensionCall;
> import javax.xml.transform.TransformerException;
> 
> public class FileSystem {
> 	//
> 	//	Elements
> 	//
> 	
> 	public void mkdir(XSLProcessorContext context, 
> ElemExtensionCall call) throws 
> javax.xml.transform.TransformerException {
> 		String path = call.getAttribute("path", 
> context.getContextNode(), context.getTransformer());
> 		
> 		if (path.equals("")) throw new 
> TransformerException("Mkdir failed: Please add a path 
> attribute.");
> 		else {
> 			File f = new File(path);
> 			if (!f.exists()) f.mkdirs();
> 		}
> 	}
> 
> 	public void copy(XSLProcessorContext context, 
> ElemExtensionCall call) throws java.io.IOException, 
> javax.xml.transform.TransformerException {
> 		String src = call.getAttribute("src", 
> context.getContextNode(), context.getTransformer());
> 		String dest = call.getAttribute("dest", 
> context.getContextNode(), context.getTransformer());
> 		String overwrite = call.getAttribute("overwrite", 
> context.getContextNode(), context.getTransformer());
> 
> 		if (src.equals("") || dest.equals("")) throw new 
> TransformerException("Copy failed: Please supply both src and 
> dest attributes.");
> 		else {
> 			File f = new File(src);
> 			File g = new File(dest);
> 			File h = new File(g.getPath());
> 
> 			if (!f.exists()) throw new 
> TransformerException("Copy failed: File \"" + src + "\" does not 
> exist.");
> 			else if (!f.canRead()) throw new 
> TransformerException("Copy failed: File \"" + src + "\" is not 
> readable.");
> 			else if (!h.isDirectory()) throw new 
> TransformerException("Copy failed: \"" + g.getPath() + "\" is 
> not a directory.");
> 			else if (!h.canWrite()) throw new 
> TransformerException("Copy failed: Unable to write to \"" + 
> g.getPath() + "\".");
> 			else if (!overwrite.equals("yes") && 
> !g.exists()) 
> throw new TransformerException("Copy failed: File \"" + 
> dest + "\" already exists and overwrite not set.");
> 			else {
> 				int c;
> 				FileInputStream in = new 
> FileInputStream(f);
> 				FileOutputStream out = new 
> FileOutputStream(g);
> 			
> 				while ((c = in.read()) != -1) 
> out.write(c);
> 				
> 				in.close();
> 				out.close();
> 			}
> 		}
> 	}
> 
> 	public void move(XSLProcessorContext context, 
> ElemExtensionCall call) throws 
> javax.xml.transform.TransformerException {
> 		String src = call.getAttribute("src", 
> context.getContextNode(), context.getTransformer());
> 		String dest = call.getAttribute("dest", 
> context.getContextNode(), context.getTransformer());
> 
> 		if (src.equals("") || dest.equals("")) throw new 
> TransformerException("Move failed: Please supply both src and 
> dest attributes.");
> 		else {
> 			File f = new File(src);
> 			File g = new File(dest);
> 			File h = new File(g.getPath());
> 
> 			if (!f.exists()) throw new 
> TransformerException("Move failed: File \"" + src + "\" does not 
> exist.");
> 			else if (!f.canRead()) throw new 
> TransformerException("Move failed: File \"" + src + "\" is not 
> readable.");
> 			else if (!h.isDirectory()) throw new 
> TransformerException("Move failed: \"" + g.getPath() + "\" is 
> not a directory.");
> 			else if (!h.canWrite()) throw new 
> TransformerException("Move failed: Unable to write to \"" + 
> g.getPath() + "\".");
> 			else f.renameTo(g);
> 		}				
> 	}
> 
> 	//
> 	//	Functions
> 	//
> 
> 	public String stripPath(String filespec) {
> 		File f = new File(filespec);
> 		return f.getName();
> 	}
> 
> 	public String stripFilename(String filespec) {
> 		File f = new File(filespec);
> 		return f.getPath();
> 	}
> }
> -------------
> 
>