WebApps: RowSet + Command [ + Interpreter]
The following pattern proves to be useful, if you want to expose your application tier to some scripting language. It will gain you a lot, if you deal with table-like structures and would like to unify the interface (lists of users, records, documents etc. where number of columns and details are varying).
RowSet interface was introduced in JDK 1.4 in javax.sql as a Bean-like access to JDBC resources. It somewhat resembles DataSet from the win32 world. This is a stripped-down version of any RowSet, that demonstrates its basic behaviour:
Once your application have implemented this interface, you can use it from within your JSP as a session bean.
Following JSP code demonstrates an implementation (for simplicity of understanding, I have used custom tag library):
Please note that once you hit Records.next() you can use your RowSet as a simple JavaBean - you just have an access to this single row. It is like making a 2D projection to a single dimension, which opens up a set of possibilities to reuse your code that deals with forms and updates. For your convinience, I've also included a no-taglib version of the JSP at the end of this article.
Note that even though the command syntax resembles an SQL statement, it is your responsibility to parse it and you are free to use any command language you want, as long as JSP developer can understand it. Given the tabular nature of the output, SQL might be a good set of keywords to start with. In this case, general set of rules will work like this (and Command Interpeter is a good pattern to proceed from here): you parse the command and pick the CommandProcessor based on the FROM clause. You might want to assume that in SELECT clause you have a list of bean properties and use reflection API to access requested data, which your CommandProcessor will return. You are also free to add any syntax constructions you want, based on your application logic. By now you might already recognize how to add ORDER BY and make column headers invoke sorting.
This pattern gains you an easy access to tabular data without sacrificing flexibility to add buffering, caching, to mix JDBC results with your business logic data etc. Originally propsed RowSet from the JDK contains some 209 methods and you might want to study them to look for some useful tips.
I welcome any notes and will probably add a diagram to demonstrate the approach from the bird perspective.
Listing 1. No taglib version of JSP.
Listing 2. Simplest implementation of the interface.
RowSet interface was introduced in JDK 1.4 in javax.sql as a Bean-like access to JDBC resources. It somewhat resembles DataSet from the win32 world. This is a stripped-down version of any RowSet, that demonstrates its basic behaviour:
1 interface SimpleRowSet
2 {
3 void execute(String command);
4 boolean next();
5 String[] columns();
6 String getString(int column);
7 }Once your application have implemented this interface, you can use it from within your JSP as a session bean.
Following JSP code demonstrates an implementation (for simplicity of understanding, I have used custom tag library):
1 <%-- Simple JSP to demonstrate RowSet functionality --%>
2 <%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*, com.myorg.myapp.*" %>
3 <%@ taglib uri="/rowset.tld" prefix="rowset" %>
4 <html>
5 <head><title>Rowset Test Page</title></head>
6 <body>
7 <table border="1">
8 <jsp:useBean id="Records"
9 class="com.myorg.myapp.MyRowSet"
10 scope="session">
11 <% Records.execute("SELECT name, calltime, telephone FROM Records"); // initialize our CachedRowSet bean %>
12 <!-- = HEADER = -->
13 <tr bgcolor="#E0E0E0">
14 <rowset:columns name='Records' tdclass='myheader'/>
15 </tr>
16 <!-- = BODY = -->
17 <% while (Records.next()) { %>
18 <tr> <rowset:values name='Records' tdclass='myvalue'/> </tr>
19 <% } %>
20 </jsp:useBean>
21 </table>
22 </body>
23 </html>Please note that once you hit Records.next() you can use your RowSet as a simple JavaBean - you just have an access to this single row. It is like making a 2D projection to a single dimension, which opens up a set of possibilities to reuse your code that deals with forms and updates. For your convinience, I've also included a no-taglib version of the JSP at the end of this article.
Note that even though the command syntax resembles an SQL statement, it is your responsibility to parse it and you are free to use any command language you want, as long as JSP developer can understand it. Given the tabular nature of the output, SQL might be a good set of keywords to start with. In this case, general set of rules will work like this (and Command Interpeter is a good pattern to proceed from here): you parse the command and pick the CommandProcessor based on the FROM clause. You might want to assume that in SELECT clause you have a list of bean properties and use reflection API to access requested data, which your CommandProcessor will return. You are also free to add any syntax constructions you want, based on your application logic. By now you might already recognize how to add ORDER BY and make column headers invoke sorting.
This pattern gains you an easy access to tabular data without sacrificing flexibility to add buffering, caching, to mix JDBC results with your business logic data etc. Originally propsed RowSet from the JDK contains some 209 methods and you might want to study them to look for some useful tips.
I welcome any notes and will probably add a diagram to demonstrate the approach from the bird perspective.
Listing 1. No taglib version of JSP.
1 <%-- Simple JSP to demonstrate RowSet functionality --%>
2 <%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*, com.myorg.myapp.*" %>
3 <html>
4 <head><title>Rowset Test Page</title></head>
5 <body>
6 <table border="1">
7 <jsp:useBean id="Records"
8 class="com.myorg.myapp.MyRowSet"
9 scope="session">
10 <%
11 // initialize our CachedRowSet bean
12 Records.execute("SELECT name, calltime, telephone FROM Records");
13 %>
14 <!-- ******* HEADER ******* -->
15 <tr bgcolor="#E0E0E0">
16 <% for (int i=0; i<Records.columns().length; i++) {%><td><%=Records.columns()[i]%></td><% } %>
17 </tr>
18 <!-- ******* BODY ******* -->
19 <%
20 while (Records.next()) {
21 %>
22 <tr>
23 <!-- *** ROW *** -->
24 <% for (int i=0; i<Records.columns().length; i++) {%><td><%=Records.getString(i)%></td><% } %>
25 </tr>
26 <% } %>
27 </jsp:useBean>
28 </table>
29 </body>
30 </html>Listing 2. Simplest implementation of the interface.
1 package com.myorg.myapp;
2
3 import java.util.List;
4 import java.util.ArrayList;
5
6 /**
7 * This is a straightforward implementation of SimpleRowSet
8 */
9 public class MyRowSet implements SimpleRowSet
10 {
11
12 private String[] _columns;
13 private List _results;
14 private int _idx;
15
16 public MyRowSet()
17 {
18 _idx = -1;
19 }
20
21 public void execute(String command)
22 {
23 if ("SELECT USERS".equals(command))
24 {
25 _columns = new String[]{"Name","Surname","Magic Number"};
26 _results = new ArrayList();
27 _results.add(new String[]{"John","Smith","1"});
28 _results.add(new String[]{"Walter","Hugh","2"});
29 }
30 else
31 if ("SELECT FILES".equals(command))
32 {
33 _columns = new String[]{"Name","Size"};
34 _results = new ArrayList();
35 //grab your files here via java.io API
36 }
37 }
38
39 public boolean next()
40 {
41 _idx++;
42 return (_results !=null && _idx<_results.size());
43 }
44
45 public String[] columns()
46 {
47 return _columns;
48 }
49
50 public String getString(int column)
51 {
52 return ((String[])_results.get(_idx))[column];
53 }
54 }