390 likes | 678 Views
XML and JSP. Alex Chaffee, alex@jguru.com http://www.purpletech.com. Core Technologies. JSP - Server-side Java XML - Data storage and exchange format. Web Sites' Needs. vast amount of data uniform presentation mostly reading, limited writing. Solution. data stored in XML files
E N D
XML and JSP Alex Chaffee, alex@jguru.com http://www.purpletech.com
Core Technologies • JSP - Server-side Java • XML - Data storage and exchange format
Web Sites' Needs • vast amount of data • uniform presentation • mostly reading, limited writing
Solution • data stored in XML files • UI presented in JSP
Why not a database? • separate process or machine • higher maintenance • SQL sucks • Object-relational mapping
Why XML? • Text files are very easy to deal with • Edit, backup, copy, download, upload with standard tools • Hierarchical data model
Why not XML? • High volume of writes • Complicated or variable queries • Failure tolerance
XML-Database Gaps • Not indexed or searchable (yet :-) • No transactions (but can do simple file locking)
Application: Online Photo Album • Picture fields - title, date, caption - image - thumbnails • Image fields - source file - height, width
Sample Data File <picture> <title>Alex On The Beach</title> <date>1999-08-08</date> <caption>Trying in vain to get a tan</caption> <image> <src>alex-beach.jpg</src> <width>340</width> <height>200</height> </image>
Sample Data File (cont.) <thumbnails> <image> <src>alex-beach-sm.jpg</src> <width>72</width> <height>72</height> </image> <image> <src>alex-beach-med.jpg</src> <width>150</width> <height>99</height> </image> </thumbnails> </picture>
Sample Filesystem summer99/alex-beach.pix summer99/alex-beach.jpg summer99/alex-beach-sm.jpg summer99/alex-beach-med.jpg summer99/alex-snorkeling.pix etc.
Techniques • Many ways to bring XML into JSP • DOM • You can use classes implementing the DOM interface to parse and inspect the XML file • JDOM • Better than DOM • XPath • You can use an XPath processor (like Resin) to locate elements in the XML file by path name • /picture[1]/thumbnails[2]/width
Techniques (cont.) • XSL • You can use an XSL processor to transform the XML into HTML • Cocoon • You can use the open-source Cocoon framework • Roll your own Bean • You can write a wrapper class that uses one of the other techniques to load the data into a custom JavaBean Note that these techniques could be applied equally well to an XML stream you receive from another source, like a client or an application server.
JSP Engine • Tomcat • It supports the most up-to-date versions of the JSP and Servlet specs • It's endorsed by Sun and Apache • You can run it "standalone" without configuring a separate Web server • It's open source
JSP Include Structure • I use an "init.jsp" file • Store common functions, imports, constants, variables, <jsp:useBean> initializers • Include using <%@include file="init.jsp"%> • Acts like C #include • For other purposes, use <jsp:include page="…"/> • Compiles included page separately
Finding the file • How do you know which file to load? • CGI • request.getParameter("file") • How do you know which directory the file is in? • Ask the servlet context • String picturefile = application.getRealPath("/" + request.getParameter("file")); • String picturefile = getServletContext().getRealPath("/" + request.getParameter("file")); • Important: validate the path • what if the value "file=../../../../etc/passwd" were entered?
The Document Object Model • DOM: W3C interfaces for accessing inside of an XML document • API is fairly cumbersome • My DOMUtils contains simple wrappers for DOM calls
Accessing the node values with DOMUtils Document doc = DOMUtils.xml4jParse(picturefile); Element nodeRoot = doc.getDocumentElement(); Node nodeTitle = DOMUtils.getChild(nodeRoot, "title"); String title = (nodeTitle == null) ? null : DOMUtils.getTextValue(nodeTitle); Node nodeImage = DOMUtils.getChild(nodeRoot, "image"); Node nodeSrc = DOMUtils.getChild(nodeImage, "src"); String src = DOMUtils.getTextValue(nodeSrc);
Embedding XML values in JSP/HTML <table bgcolor="#FFFFFF" border="0" cellspacing="0" cellpadding="5"> <tr> <td align="center" valign="center"> <img src="<%=src%>" width="<%=width%>" height="<%=height%>" border="0" alt="<%=src%>"></td> </tr> </table>
Full Source Code See picture-dom.jsp
JSP Beans • Problem with above: commingles presentation and data • Embeds Java code directly in JSP scriptlet • Cleaner approach: use JSP Beans • Scriptlets good for control flow, minor variable access, prototyping • JavaBeans for significant Java code • database access, business logic, other algorithms • Prototype with scriptlets, polish with Beans • More modular • Can reuse code • But wait until you have code worth reusing
Picture Beans • Picture.java, Image.java, etc. • constructors or setter methods that take in a DOM node or a file name from which to extract their values • get/set methods for each sub-element
Picture.java (interface) public interface Picture { public void setTitle(String title); public String getTitle(); public void setDate(String date); public String getDate(); public void setCaption(String caption); public String getCaption(); public void setImage(Image image); public Image getImage(); public void setThumbnails(Thumbnails thumbnails); public Thumbnails getThumbnails(); }
DOMPicture.java (implementation) public class DOMPicture implements Picture { private String title; private String date; private String caption; private Image image; private Thumbnails thumbnails; public DOMPicture() {} public DOMPicture(Node node) { setNode(node); }
DOMPicture (cont.) public void setNode(Node node) { Node nodeTitle = DOMUtils.getChild(node, "title"); setTitle((nodeTitle == null) ? null : DOMUtils.getTextValue(nodeTitle)); Node nodeCaption = DOMUtils.getChild(node, "caption"); setCaption((nodeCaption == null) ? null : DOMUtils.getTextValue(nodeCaption)); Node nodeDate = DOMUtils.getChild(node, "date"); setDate((nodeDate == null) ? null : DOMUtils.getTextValue(nodeDate)); Node nodeImage = DOMUtils.getChild(node, "image"); setImage(new DOMImage(nodeImage)); Node nodeThumbnails = DOMUtils.getChild(node, "thumbnails"); setThumbnails(new DOMThumbnails(nodeThumbnails)); }
DOMPicture (cont.) public void setTitle(String title) { this.title = title; } public String getTitle() { return title; } public void setDate(String date) { this.date = date; } public String getDate() { return date; } etc…
Source Code Notes • I defined interfaces separately from implementation classes, so you are free to choose alternate implementations in the future. You may want to store the values in a List, or in the DOM itself, or even a database. • The beans are defined in a custom package, picturebeans. All JSP beans must be in a package (most JSP engines won't be able to find classes that are in the default package). • I provided set methods in addition to get methods. At the moment, you're only reading; however, in the future, you may want to let users edit pictures, so you're planning for the ability to change and write properties. • I now have to say "<%=picture.getCaption()%>" instead of just "<%=caption%>", since the values are stored in a bean rather than in local variables. However, if you want, you can define local variables like "String caption = picture.getCaption();". This is acceptable since it makes the code a little easier to read and understand.
Bang, Zoom, to the Moon • define a parameter, zoom, whose value determines which of the thumbnail images to display • clicking on a "Zoom In" or "Zoom Out" button will select the next or previous thumbnail in the list • finding the right thumbnail • (Image)picture.getThumbnails().get(i) • "Zoom In" and "Zoom Out" links • generate a recursive reference to the same page, with different parameters. • request.getRequestURI() <% if (zoom < (thumbnails.size() -1)) { out.print("<a href='" + request.getRequestURI() + "?file=" + request.getParameter("file") + "&zoom=" + (zoom+1) + "'>"); out.print("Zoom In</a>"); } %>
JSP Bean Tags • <jsp:useBean> can replace embedded Java code • Why bother? • The tag syntax is arguably less intimidating to HTML designers. • useBean has a scope parameter that automatically figures out whether the bean should be stored as a local variable, a session variable, or an application attribute. • If the variable is persistent (session or application), useBean initializes it if necessary, but fetches the variable if it already exists. • It's potentially more portable to future versions of the JSP spec, or alternate implementations (for example, a hypothetical JSP engine that stores variables in a database, or shares them across server processes).
Picture App with <jsp:useBean> <jsp:useBean id="picture" scope="request" class="picturebeans.DOMPicture"> <% Document doc = DOMUtils.xml4jParse(picturefile); Element nodeRoot = doc.getDocumentElement(); nodeRoot.normalize(); picture.setNode(nodeRoot); %> </jsp:useBean> • or, if you define a setFile(String) method inside DOMBean: <jsp:useBean id="picture" scope="request" class="picturebeans.DOMPicture"> <jsp:setProperty name="picture" property="file" value="<%=picturefile%>"/> </jsp:useBean>
Caching • XML Parsing takes time • You must cache the results of a DOM traversal for later requests for the same file • My CachedFS.java keeps a cache • Also checks if the file has changed, reloads if necessary • So does JDOM Bean • Define it as an application-scope bean • In init.jsp, so you centralize the initialization code
Using FSCache <jsp:useBean id="cache" class="com.purpletech.io.CachedFS" scope="application"> <% cache.setRoot(application.getRealPath("/")); cache.setLoader( new CachedFS.Loader() { // load in a single Picture file public Object process(String path, InputStream in) throws IOException { try { Document doc = DOMUtils.xml4jParse (new BufferedReader(new InputStreamReader(in))); Element nodeRoot = doc.getDocumentElement(); nodeRoot.normalize(); Picture picture = new DOMPicture(nodeRoot); return picture; } catch (XMLException e) { e.printStackTrace(); throw new IOException(e.getMessage()); } } }); %> </jsp:useBean>
XPath • a simple syntax for locating nodes in an XML tree • easier to use than DOM • embed the entire path in a string -- for example, "/picture/thumbnails/image[2]". • Resin by Caucho ncludes an XPath processor • can use the Caucho XPath object on its own, without buying into the rest of the Resin framework. • Node verse = XPath.find("chapter/verse", node); • Can put it in Picture Beans, and/or in JSP
XSL • Different style of solution to same problem • Possible to combine with this technique • Different philosophy: • JSP treats HTML as primary • XSL treats template rules (fragments) as primary
XML Data Binding • Lots of other approaches • Adelard: JSR 031 • See my XML and Java talk