Applications


In this section we’ll look at a common use for Xalan. Let’s say you’re building a Web application, and you want to serve your data as XML. Maybe you have an XML-enabled database that spits out XML, or perhaps your content-management system generates XML. It doesn’t really matter. You can use XSLT to transform that XML into something appropriate for the user agent visiting the Website. You can generate HTML specific for a particular browser, or you could generate WML if you were being visited by a cell phone.

You’ll write a servlet filter that sits in front of a servlet and intercepts the servlet’s request and response to augment the servlet’s functionality. In this case, you intercept the request and figure out what kind of client is accessing the site. After the servlet has done its job, the filter intercepts the response, which is an XML document, and uses Xalan to transform the result in a manner appropriate to the client.

The first thing you need is a servlet you can put the filter in front of. This servlet picks up an XML file (in this case, the now-familiar book.xml file) and blasts it back out as the result:

  1: /*   2:  *    3:  * XMLServlet.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8:    9: import java.io.IOException;  10: import java.io.InputStream;  11: import java.io.OutputStream;  12: import javax.servlet.ServletException;  13: import javax.servlet.http.HttpServlet;  14: import javax.servlet.http.HttpServletRequest;  15: import javax.servlet.http.HttpServletResponse;  16:   17: public class XMLServlet extends HttpServlet {  18:   19:     protected void doGet(HttpServletRequest req,   20:                           HttpServletResponse res)  21:         throws ServletException, IOException {  22:         OutputStream os = res.getOutputStream();  23:           24:         byte buffer[] = new byte[4096];  25:         InputStream is =   26:             getServletContext().getResourceAsStream("books.xml");  27:         int count = is.read(buffer);  28:         os.write(buffer,0,count);  29:     }  30:   31: }

Here’s the web.xml deployment descriptor for the Web application. It maps the servlet to a URI and also tells the Web container that a filter is attached to the servlet:

  1: <?xml version="1.0" encoding="ISO-8859-1"?>   2:    3: <!DOCTYPE web-app   4:   PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"   5:   "http://java.sun.com/dtd/web-app_2_3.dtd">   6:    7: <web-app>   8:   <display-name>   9:    Professional XML Development with Apache Tools Examples  10:   </display-name>  11:   <description>  12:     Examples from Professional XML Development with Apache Tools.  13:   </description>  14:   15:   <!— Define servlet-mapped and path-mapped example filters —>  16:   <filter>  17:     <filter-name>XSLT Filter</filter-name>  18:     <filter-class>XSLTServletFilter</filter-class>  19:   </filter>  20:   21:   <!— Define filter mappings for the defined filters —>  22:   <filter-mapping>  23:     <filter-name>XSLT Filter</filter-name>  24:     <servlet-name>XMLServlet</servlet-name>  25:   </filter-mapping>  26:   27:   <servlet>  28:     <servlet-name>XMLServlet</servlet-name>  29:     <servlet-class>XMLServlet</servlet-class>  30:   </servlet>  31:       32:   <servlet-mapping>  33:     <servlet-name>XMLServlet</servlet-name>  34:     <url-pattern>/servlet/*</url-pattern>  35:   </servlet-mapping>  36: </web-app>

Now you’re ready for the filter. The filter uses Xalan in Smart Transformer mode. You need to use templates because you’re in a multithreaded situation. You should also be careful about the amount of stylesheet compilation and loading, so you implement an application-wide cache for all the templates you use:

  1: /*   2:  *    3:  * XSLTServletFilter.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8: import java.io.CharArrayWriter;   9: import java.io.IOException;  10: import java.io.PrintWriter;  11: import java.io.StringReader;  12: import java.util.Collections;  13: import java.util.HashMap;  14: import java.util.Map;  15: import java.util.Properties;  16:   17: import javax.servlet.Filter;  18: import javax.servlet.FilterChain;  19: import javax.servlet.FilterConfig;  20: import javax.servlet.ServletContext;  21: import javax.servlet.ServletException;  22: import javax.servlet.ServletRequest;  23: import javax.servlet.ServletResponse;  24: import javax.servlet.http.HttpServletRequest;  25: import javax.servlet.http.HttpServletResponse;  26: import javax.xml.transform.Source;  27: import javax.xml.transform.Templates;  28: import javax.xml.transform.Transformer;  29: import javax.xml.transform.TransformerConfigurationException;  30: import javax.xml.transform.TransformerFactory;  31: import javax.xml.transform.stream.StreamResult;  32: import javax.xml.transform.stream.StreamSource;  33:   34: public class XSLTServletFilter implements Filter {  35:     FilterConfig config = null;  36:     Map transletCache = null;  37:   38:     public void init(FilterConfig fc) throws ServletException {  39:         config = fc;  40:           41:         ServletContext ctx = fc.getServletContext();   42:           43:         transletCache =   44:             (Map) ctx.getAttribute("transletCache");  45:           46:         if (transletCache == null) {  47:             transletCache =   48:                 Collections.synchronizedMap(new HashMap());  49:             ctx.setAttribute("transletCache", transletCache);  50:         }

In lines 41-50, the filter looks for the application-wide translet cache. If it doesn’t exist, the filter creates one. You use a synchronized HashMap for the cache. In lines 52-57, you set up Xalan for Smart Transformer mode:

 51:           52:         String KEY = "javax.xml.transform.TransformerFactory";  53:         String VALUE =   54:     "org.apache.xalan.xsltc.trax.SmartTransformerFactoryImpl";  55:         Properties sysProps = System.getProperties();  56:         sysProps.put(KEY,VALUE);  57:         System.setProperties(sysProps);  58:     }  59: 

All the action happens in doFilter:

 60:     public void doFilter(  61:         ServletRequest req,  62:         ServletResponse res,  63:         FilterChain chain)  64:         throws IOException, ServletException {  65:         String contentType;  66:         String styleSheet;  67:   68:         HttpServletRequest httpReq = (HttpServletRequest) req;  69:         String userAgent = httpReq.getHeader("User-Agent");  70:         contentType = selectContentType(userAgent);  71:         res.setContentType(contentType);

You intercept the request and find out what the user agent (browser) is. Then you pass that information to a function that figures out the right content type for the data the filter/servlet combination returns.

The BufferedReponseWrapper class looks like a ServletResponse. It captures all the data written into the wrapper in a buffer so the filter can manipulate it after the servlet has generated it:

 72:   73:         PrintWriter out = res.getWriter();  74:   75:         BufferedResponseWrapper wrappedResponse =  76:             new BufferedResponseWrapper((HttpServletResponse)res);

Here’s where the filter calls the servlet to do its job. The servlet executes in complete ignorance of the filter:

 77:   78:         chain.doFilter(req, wrappedResponse);

Now you get the buffer from the response wrapper and use it to create a StreamSource that can be used as input to a transformer:

 79:   80:         String s = new String(wrappedResponse.getBuffer());  81:         StringReader sr =  82:             new StringReader(s);  83:         Source xmlSource = new StreamSource(sr);

You also select the right translet based on what the user agent was:

 84:   85:         try {  86:             Templates translet = selectTemplates(userAgent);

The next two lines (87-88) solve some impedance mismatch problems between the transformer and the filter:

 87:             CharArrayWriter caw = new CharArrayWriter();  88:             StreamResult result = new StreamResult(caw);  89:             Transformer xformer = translet.newTransformer();  90:             xformer.transform(xmlSource, result);

Now that the result is set up, you create a transformer from the translet/templates and perform the transformation. Then you compute the length of the output (line 91) and blast the results out to the user agent:

 91:             res.setContentLength(caw.toString().length());  92:             out.write(caw.toString());  93:         } catch (Exception ex) {  94:             out.println(ex.toString());  95:             out.write(wrappedResponse.toString());  96:         }  97:     }  98:   99:     private String selectContentType(String ua) { 100:         String contentType = ""; 101:         if (ua.indexOf("MSIE") != -1) { // IE 102:             contentType = "text/html";     103:         } else if (ua.indexOf("Firebird") != -1) { // Firebird 104:             contentType = "text/html"; 105:         } else if (ua.indexOf("Gecko") != -1) { // Mozilla 106:             contentType = "text/html"; 107:         } 108:         return contentType; 109:     }

For the sake of example, you hard-coded the conditional selection of the contentType. For a production system, you’d probably put this information in a table and do a lookup. The same is true for selectStylesheet, which figures out the filename for the stylesheet to be used:

110:  111:     private String selectStylesheet(String ua) { 112:         String stylesheet = ""; 113:         if (ua.indexOf("MSIE") != -1) { // IE 114:             stylesheet = "books-ie.xslt";     115:         } else if (ua.indexOf("Firebird") != -1) { // Firebird 116:             stylesheet = "books-firebird.xslt"; 117:         } else if (ua.indexOf("Gecko") != -1) { // Mozilla 118:             stylesheet = "books-mozilla.xslt"; 119:         } 120:         return stylesheet; 121:     } 122:      123:     private Templates selectTemplates(String ua) { 124:         Templates translet = null; 125:          126:         TransformerFactory xFactory =  127:             TransformerFactory.newInstance(); 128:         String styleSheet = selectStylesheet(ua); 129:          130:         translet = (Templates) transletCache.get(styleSheet); 131:         if (translet != null) { 132:             return translet; 133:         }

After you determine which stylesheet should be used, you check the cache to see whether you already have a translet for it. If so, you’re done. Otherwise you have to compile it:

134:          135:         try { 136:             String stylePath =  137:              config.getServletContext().getRealPath(styleSheet); 138:             Source styleSource = new StreamSource(stylePath); 139:             translet =  140:                 xFactory.newTemplates(styleSource); 141:         } catch (TransformerConfigurationException e) { 142:             e.printStackTrace(); 143:         } 144:         transletCache.put(styleSheet, translet); 

After you compile the translet, you put it in the cache and then hand it back so it can be used:

145:         return translet; 146:     } 147:      148:     public void destroy() { 149:     } 150: }

BufferedResponseWrapper is an HttpServletResponseWrapper that contains a ByteArrayOutputStream that’s used to buffer the results of the servlet so the filter can get them. The getBuffer method is the only addition to the HttpServletResponseWrapper API:

  1: /*   2:  *    3:  * BufferedResponseWrapper.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8:    9: import java.io.ByteArrayOutputStream;  10: import java.io.IOException;  11: import java.io.PrintWriter;  12:   13: import javax.servlet.ServletOutputStream;  14: import javax.servlet.http.HttpServletResponse;  15: import javax.servlet.http.HttpServletResponseWrapper;  16:   17: public class BufferedResponseWrapper   18:     extends HttpServletResponseWrapper {  19:           20:     ByteArrayOutputStream bufferedStream =   21:         new ByteArrayOutputStream();  22:       23:     public BufferedResponseWrapper(HttpServletResponse res) {  24:         super(res);  25:     }  26:   27:     public ServletOutputStream getOutputStream()   28:         throws IOException {  29:         return new ServletByteArrayOutputStream(bufferedStream);  30:     }  31:   32:   33:     public PrintWriter getWriter() throws IOException {  34:         return new PrintWriter(bufferedStream);  35:     }  36:   37:     public byte[] getBuffer() {  38:         return bufferedStream.toByteArray();  39:     }  40: } 

ServletByteArrayOutputStream is necessary because you must implement the getOutputStream method of HttpServletResponseWrapper. You want to make sure the ServletByteArrayOutputStream shares the same ByteArrayOutputStream used by the BufferedResponseWrapper:

  1: /*   2:  *    3:  * ServletByteArrayOutputStream.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8:    9: import java.io.ByteArrayOutputStream;  10: import java.io.IOException;  11:   12: import javax.servlet.ServletOutputStream;  13:   14: public class ServletByteArrayOutputStream   15:     extends ServletOutputStream {  16:   17:     ByteArrayOutputStream baos = null;  18:       19:     public ServletByteArrayOutputStream() {  20:         baos = new ByteArrayOutputStream();  21:     }  22:       23:     public ServletByteArrayOutputStream(ByteArrayOutputStream   24:                                          baos) {  25:         this.baos = baos;  26:     }  27:   28:     public void write(int i) throws IOException {  29:         baos.write(i);  30:     }  31:       32:     public byte[] getBuffer() {  33:         return baos.toByteArray();  34:     }  35:       36: }

There you have it—a servlet filter that takes XML output from a servlet and transforms it for the type of client visiting the servlet. This example should give you some ideas about how to incorporate Xalan into your own applications.




Professional XML Development with Apache Tools. Xerces, Xalan, FOP, Cocoon, Axis, Xindice
Professional XML Development with Apache Tools: Xerces, Xalan, FOP, Cocoon, Axis, Xindice (Wrox Professional Guides)
ISBN: 0764543555
EAN: 2147483647
Year: 2003
Pages: 95

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net