Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 20 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
20
Dung lượng
436,42 KB
Nội dung
public void handleAction(Node result) { Document doc=result.getOwnerDocument(); DocumentFragment queryActionFrag=doc.createDocumentFragment(); XSQLPageRequest pageRequest=getPageRequest(); Element queryElem=doc.createElement(“my-special-query”); Text queryStr=doc.createTextNode(“SELECT name FROM product”); queryElem.appendChild(queryStr); queryAction=new XSQLQueryHandler(); queryAction.init(pageRequest,queryElem); try { queryAction.handleAction(queryActionFrag); } catch (SQLException e) { reportError(result,e.getMessage()); } result.appendChild(queryActionFrag); } } In this case, you create the element named my-special-query and hard-code the SQL that you wish to pass. The result will be all of the names for all of the products. Since you don’t use the action element at all, you can invoke the action handler with the following simple XSQL: <?xml version=”1.0” encoding=”UTF-8”?> <xsql:action handler=”NoXsqlQuery” xmlns:xsql=”urn:oracle-xsql” connection=”momnpup”/> Hopefully, this discussion has given you a firm grasp of the various ways that you can reuse the built-in action handlers within your own action handlers. The process is simple and flexible. You’ve seen how to call multiple action handlers and even call action handlers that don’t have their own element in the XSQL page. You’ll see more use of built-in action handlers as the chapter progresses. JDBC Connections In the previous discussion, you used built-in action handlers to work with this the database. This approach is very easy if you wish to attach the result to the datagram. It hides the complexities of JDBC entirely. But there are many good reasons that you 500 Chapter 18 might need to use JDBC directly. If you wish to use the results in an intermediate step prior to returning data, then it may be easier to use a JDBC connection. This discussion examines the best way to do this. The following class uses the page’s JDBC connection to execute a simple SQL state- ment. The connection is named in the XSQL page and is already part of the JDBC con- nection pool. This is much easier than creating a JDBC connection from scratch and having to deal with connection pooling issues yourself. It is also easier to configure. The invoking XSQL page specifies the connection name and the details are kept in the XSQLConfig.xml file. import oracle.xml.xsql.XSQLActionHandlerImpl; import oracle.xml.xsql.XSQLActionHandler; import oracle.xml.xsql.XSQLPageRequest; import org.w3c.dom.Node; import org.w3c.dom.Element; import org.w3c.dom.Document; import org.w3c.dom.Text; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Connection; import java.sql.SQLException; public class JdbcHandler extends XSQLActionHandlerImpl { Connection conn; public void init(XSQLPageRequest req, Element action) { super.init(req,action); conn=req.getJDBCConnection(); } You grab the connection from the XSQLPageRequest object. You don’t have to do this in the init method, but it is a convenient place to do it. The connection will be null if there is no connection specified in the XSQL page. When you go to use the con- nection, you need to check to see it is null and generate an appropriate error message if it is. public void handleAction(Node result) { Document doc=result.getOwnerDocument(); Element resultRoot=doc.createElement(“result-root”); if (conn!=null) { try { Custom Action Handlers 501 String sqlStr=”SELECT name FROM product”; Statement sql=conn.createStatement(); ResultSet resultSet=sql.executeQuery(sqlStr); while (resultSet.next()){ String val=resultSet.getString(“name”); Element nameElem=doc.createElement(“NAME”); Text tNode=doc.createTextNode(val); nameElem.appendChild(tNode); resultRoot.appendChild(nameElem); resultSet.next(); } result.appendChild(resultRoot); } catch (SQLException e) { reportError(result,e.getMessage()); } } else { reportError(result,”No Database Connection”); } } } The handleAction method creates a simple statement, executes it, and grabs the result. Once you have the connection object, the full power of JDBC will be at your fin- gertips. You can do whatever you need to do. What may be new is building XML out of the result set data. This is a simple example in which an element is created for each name returned in the query. In the following text, you’ll see how you can avoid this exercise entirely by using the XSU classes. Using the XSU classes The XSU classes allow you to imitate the behavior of the xsql:query and xsql:dml actions programmatically. They take you a step deeper than reusing the built-in actions. When you use the OracleXMLQuery and OracleXMLSave classes, you don’t have to pass them an element object. Instead, you pass the SQL statement that you want, and the class returns a document. Then you just merge the document with the datagram. This example uses the oracle.xml.sql.query.OracleXMLQuery class, which you use for select statements. The oracle.xml.sql.dml.OracleXMLSave class is used for DML statements. Only the handleAction method is shown. As in the pre- vious example, the connection object, conn, is acquired in the init method. public void handleAction(Node result) { XMLDocument doc=(XMLDocument)result.getOwnerDocument(); Element resultRoot=doc.createElement(“result-root”); if (conn!=null) { 502 Chapter 18 try { // Get an XML document based on a SQL query String sqlStr=”SELECT name FROM product”; OracleXMLQuery xQ=new OracleXMLQuery(conn, sqlStr); Document queryResultDoc=xQ.getXMLDOM(); //Merge the resulting document in to the datagram Element queryRoot=queryResultDoc.getDocumentElement(); Node n=doc.adoptNode(queryRoot); result.appendChild(n); } catch (Exception e) { reportError(result,e.getMessage()); } } else { reportError(result,”No Database Connection”); } } The result of this query is exactly the same as a plain xsql:query action with the same SQL query. Each row is contained in a ROW element, and all of the ROW elements are contained in a ROWSET element. If you wish, you can set the name for the rowset element and the row element just as you can with xsql:query action. All of the other options of the xsql:query action are available. The same is true for OracleXMLSave. This example also contains a good example of when Oracle’s XMLDocument class comes in handy. When you go to merge the documents, you can easily do so by using the adoptNode method of XMLDocument. Merging the document strictly through DOM is quite a bit harder. Adding XMLType Objects In the previous discussion, you merged two XML documents together. If you are stor- ing XML documents in Oracle, this problem can arise often. You saw an example of this in the application that you built earlier in the book with the product document inter- face. In that case, you used the xsql:include-owa action along with a PL/SQL pro- cedure to output the XML to the datagram. In an action handler, you don’t have to do this. Instead, you create a document and merge it in to the document. The following example shows you how to do this. You grab your JDBC connection as before. Then, you use the extract() function of the XMLType to get the data. public void handleAction(Node result) { XMLDocument doc=(XMLDocument)result.getOwnerDocument(); Element resultRoot=doc.createElement(“product-set”); if (conn!=null) { try { Custom Action Handlers 503 // execute the query String sqlStr=”SELECT name,p.doc.extract(‘/product’).getStringVal() AS product_xml FROM product p”; Statement sql=conn.createStatement(); ResultSet resultSet=sql.executeQuery(sqlStr); while (resultSet.next()){ String xmlStr=resultSet.getString(“product_xml”); InputSource xmlIn=new InputSource(new StringReader(xmlStr)); // parse the xml string JXDocumentBuilderFactory dBF=new JXDocumentBuilderFactory(); DocumentBuilder docBuilder=dBF.newDocumentBuilder(); Document xDoc=docBuilder.parse(xmlIn); // merge the documents Element productRoot=xDoc.getDocumentElement(); Node n=doc.adoptNode(productRoot); resultRoot.appendChild(n); } result.appendChild(resultRoot); } catch (Exception e) { reportError(result,e.getMessage()); } } else { reportError(result,”No Database Connection”); } } The query is executed as in the straight JDBC example earlier. But the value can’t be added to the datagram as a string. All of the special characters would be escaped and you wouldn’t be able to use transformations against the XML. Instead, you parse the string into an XML document and then merge the two documents. Parameters and Input In the previous two sections, you learned how to attach XML to the datagram. This is only one part of action handler programming. You can also get and set the parameters of the XSQL page and a stylesheet, as well as access all of the attributes and text of the invoking xsql:action element. XSQL doesn’t limit you to just the XSQL page. You’ll learn how to access initialization parameters from the XSQLConfig.xml file. The last two parts of this section reveal how to interpolate parameters. You saw earlier that a built-in action element knows how to interpolate parameters found in the 504 Chapter 18 xsql:action element. Now, you’ll see how to handle parameters in your own action handlers. As you read this section, you’ll see that XSQL gives you many ways to control your custom action handlers. You already know how to access all the information of a servlet; you’re about to learn how to get input from the XSQL page and the XSQLCon- fig.xml, also. But that’s only half the story. You can use both the XSQL parameters and the stylesheet parameters as a powerful output mechanism. By setting a parame- ter on a page, your action handler can communicate data with other action handlers. Now, it’s time to conquer parameters and input! This subject material might not seem as important as accessing the database and pushing data to the datagram. But here you understand how to control and modularize your action handlers and how your action handler can control other action handler’s behavior. Just think: You could write the next xsql:query! Accessing XSQL Data In the previous section, you saw that a built-in action handler class can make use of the values of the action element. You pass the action element to the XSQLActionHandler object and it reads the data. But what if you aren’t using a built-in action handler or if some of your code needs to access values of the elements directly? The XmlTypeHandler class developed earlier in the chapter is a good example for this. That class grabs sets of XML documents stored as XMLTypes and appends them to the datagram. But the SQL used to get the documents was hard-coded. Thus, the action handler was invoked like this from the XSQL page: <xsql:action handler=”XmlTypeHandler”/> That’s no good. If you want to use different SQL, you’ll have to recode your action han- dler. One of the key benefits of XSQL is that you can keep the SQL statements outside of your compiled code. For something as generically applicable as the XMLTypeHandler, you want to be able to do something like the following: <xsql:action handler=”XmlTypeHandler” root-element-name=”products”> SELECT name, p.doc.extract(‘/product’).getStringVal() AS product_xml FROM product p </xsql:action> The goal is that you can read the SQL statement in just as any other action handler can. The XSQL page author can also set the name of the root element that will be returned. Of course, you also want to be able to use XSQL parameters in the action ele- ment. You’ll learn how to substitute parameter values in the next section. For this dis- cussion, the focus is on accessing the values. Custom Action Handlers 505 Your first step is to modify the init() method so that you grab the action element and the XSQLPageRequest object. Note that this time you cast the action element to an XSQLElement variable. You’ll see why in a moment. public class XmlTypeHandler extends XSQLActionHandlerImpl { Connection conn; XMLElement actionElem; XSQLPageRequest pageRequest; public void init(XSQLPageRequest req, Element action) { super.init(req,action); this.actionElem=(XMLElement)action; this.pageRequest=req; this.conn=req.getJDBCConnection(); } Now that you have the element captured, you’ll want to grab the text value of the action element and the root-element-name attribute value. Here’s how you do it: public void handleAction(Node result) { try { String sqlStr=actionElem.valueOf(“.”); String resultRootName=getAttribute(“root-element-name”,actionElem); resultRootName=(resultRootName==null || resultRootName.length()==0? “xml-doc-set”:resultRootName); From here, the code is like before. The difference is that now you are working with an SQL statement and resultRootName that are derived from the invoking XSQL page. XMLDocument doc=(XMLDocument)result.getOwnerDocument(); Element resultRoot=doc.createElement(resultRootName); if (conn!=null) { Statement sql=conn.createStatement(); ResultSet resultSet=sql.executeQuery(sqlStr); while (resultSet.next()){ String xmlStr=resultSet.getString(“product_xml”); InputSource xmlIn=new InputSource(new StringReader(xmlStr)); // parse the xml string 506 Chapter 18 JXDocumentBuilderFactory dBF=new JXDocumentBuilderFactory(); DocumentBuilder docBuilder=docBuilderFactory.newDocumentBuilder(); Document xDoc=docBuilder.parse(xmlIn); // merge the documents Element productRoot=xDoc.getDocumentElement(); Node n=doc.adoptNode(productRoot); resultRoot.appendChild(n); } result.appendChild(resultRoot); } else { reportError(result,”No Database Connection”); } } catch (Exception e) { reportError(result,e.getMessage()); } } } In this example, you grabbed the text value of the action element. If your action element has child elements, you can certainly grab the values of those as well. For instance, if the name of a child element is “child”, you can do the following to grab the value: String s=actionElem.valueOf(“child”); You’ll learn more about creating nested action elements in the section after this next one. Now it’s time to complete this example by making the preceding example param- eter ready. Substituting Parameter Values One of the nicest features of XSQL is that you can pass parameters to action handlers. You can put them in the value of an attribute or in the text of an element itself. If you are writing an action handler, you want to have the same kind of power. For instance, it would be best if you could invoke the XmlHandler as follows: <xsql:action handler=”XmlTypeHandler” root-name=”product-set”> SELECT name, p.doc.extract(‘{@xPath-exp}’).getStringVal() AS product_xml FROM product p WHERE p.id={@product_id} </xsql:action> Custom Action Handlers 507 Oracle provides a couple of ways to substitute the parameters easily. For attributes, all you have to do is call the getAttributeAllowingParam() method. If there is no parameter set, you’ll need to use a line like the second one to set the default value. String resultRootName=getAttributeAllowingParam(“root-name”,actionElem); resultRootName=(resultRootName==null || resultRootName.length()==0? “xml-doc-set”:resultRootName); You can get the same behavior by using XSQLUtil.resolveParams(). This is useful if you want to know what the original value of the attribute is without parsing. String rootNameStr=getAttribute(“root-name”,actionElem); String rootNameVal=XSQLUtil.resolveParams(rootNameStr, pageRequest); rootNameVal=(rootNameVal==null || rootNameVal.length()==0? “xml-doc-set”:rootNameVal); This takes care of attributes. The next step is to handle the text of an element. Again, there are two ways to do this. If you are interested in resolving just the text value of the action element, you can do that with the XSQLActionHandlerImpl method getActionElementContent(). normalizes the text and returns a string with all parameters already substituted. String s=getActionElementContent(); This method works only if you want the immediate child text of an element. What if the action handler has children and you want to access their text? Then you can use the XSQLUtil.resolveParams() method after grabbing the text. XMLElement actionElem=(XMLElement)getActionElement(); String s=actionElem.valueOf(“child”); String s=XSQLUtil.resolveParams(s, getPageRequest()); The following example shows you how to recursively descend an element, resolving all of the parameters. If you want children for your action handler elements, you’ll need something like this. It takes a pure DOM approach. public static void resolveElementParams(Element newElem, XSQLPageRequest pageRequest) { NamedNodeMap attribs=newElem.getAttributes(); for (int i=0;i<attribs.getLength();i++) { Node attrib=attribs.item(i); String valStr=attrib.getNodeValue(); valStr=XSQLUtil.resolveParams(valStr,pageRequest); attrib.setNodeValue(valStr); } 508 Chapter 18 The preceding code resolves any attributes that the element may have. The next set of code works through all the children nodes. If they are text, the parameters will be resolved. NodeList list=newElem.getChildNodes(); for (int i=0;i<list.getLength();i++) { Node n=list.item(i); // Text node if (n.getNodeType()==Node.TEXT_NODE) { log.println(“found text node”); String valStr=n.getNodeValue(); log.println(“valStr==”+valStr); valStr=XSQLUtil.resolveParams(valStr,pageRequest); log.println(“valStr replaced ==”+valStr); n.setNodeValue(valStr); } The last two lines take care of the recursion. If the element has a child element, the method is called again on the child. The recursion stops and the method halts when no more children are encountered. if (n.getNodeType()==Node.ELEMENT_NODE) { resolveElementParams((Element)n,pageRequest); } } } This method gives you more flexibility when designing action handlers. For instance, you can invoke an action handler with this method. The hypothetical case is that your action handler chooses what action to take based on the test attribute. These could result in different SQL queries or maybe calls to other data sources. The problem solved is one of complexity. If you need to pass a lot of information to an action han- dler, why not use XML to help you keep it organized? This can be a more sane approach than having to parse text inside your action handlers. <xsql:action handler=”SomeActionHandler”> <firstChoice test=”{@test1}”>{@valParam1}</oneChoice> <secondChoice test=”{@test2}”>{@valParam2}</secondChoice> <thirdChoice test=”{@test3}”>{@valParam3}</thirdChoice> <otherwise>{@valParam4}</otherwise> </xsql:action> In the Java code, you clone the action element first, then the resolveElement- Params: Element elem=getActionElement().cloneNode(true); resolveElementParams(elem,pageRequest); Custom Action Handlers 509 [...]... it as input for queries, dml, or any other type of action This example code allows you to set any arbitrary header value as a parameter in your page import oracle. xml.xsql.XSQLActionHandlerImpl; import oracle. xml.xsql.XSQLServletPageRequest; import oracle. xml.xsql.XSQLPageRequest; import org.w3c.dom.Node; import org.w3c.dom.Element; import javax.servlet.http.HttpServletRequest; public class GetHttpHeader... of a remote user if HTTP authentication is used The following action handler makes all the various request variables available as page parameters: import oracle. xml.xsql.XSQLActionHandlerImpl; import oracle. xml.xsql.XSQLServletPageRequest; import oracle. xml.xsql.XSQLPageRequest; import org.w3c.dom.Node; import org.w3c.dom.Element; import javax.servlet.http.HttpServletRequest; public class GetRequestVariable... request, it creates it with a count set to zero Each time this action handler is invoked, it increments the count and appends the value it set as an element in the datagram import oracle. xml.xsql.XSQLActionHandlerImpl; import oracle. xml.xsql.XSQLPageRequest; import org.w3c.dom.Node; import org.w3c.dom.Element; import javax.servlet.http.HttpServletRequest; public class Counter extends XSQLActionHandlerImpl... servletPR.setPageParam(paramName,headerValue); } } } } Custom Action Handlers You can invoke this code with the following XSQL page, which uses the action handler The parameter host-param will hold the value... that you desire Here is a simple example where you can specify that you want any header displayed just by setting the header name in the URL: The header values aren’t the most interesting values... pageRequest.setRequestObject(“count”,new Integer(count)); addResultElement(result,”count”,””+count); } } When you invoke the action handler as follows, . document with the datagram. This example uses the oracle. xml.sql.query.OracleXMLQuery class, which you use for select statements. The oracle. xml.sql.dml.OracleXMLSave class is used for DML statements details are kept in the XSQLConfig.xml file. import oracle. xml.xsql.XSQLActionHandlerImpl; import oracle. xml.xsql.XSQLActionHandler; import oracle. xml.xsql.XSQLPageRequest; import org.w3c.dom.Node; import. header value as a parameter in your page. import oracle. xml.xsql.XSQLActionHandlerImpl; import oracle. xml.xsql.XSQLServletPageRequest; import oracle. xml.xsql.XSQLPageRequest; import org.w3c.dom.Node; import