Method for verifying context between multiple related XML tags in document object model (DOM)6718516Abstract A method for processing a Document Object Model (DOM) tree to verify context between multiple related XML tags. One or more of these related XML tags are custom tags. According to the invention, contextual relationships between the related XML tags are verified using the DOM itself to indicate state. In a preferred embodiment, the inventive method begins during the processing of the DOM tree with a current element being processed replacing itself with a placeholder element. The placeholder element includes attributes indicating its state. If a clean-up element does not already exist for the element being processed, the current element creates a clean-up element and adds it to the DOM, for example, as a child node to the root position. When the clean-up element is later encountered, this element scans the entire DOM for all the related tags (now placeholders) of interest. The clean-up element loads the state information from each and processes the state information accordingly. When complete, the clean-up element removes itself from the DOM. Claims What is claimed is: Description This application contains subject matter protected by Copyright Law. All rights reserved.
public interface TagBean
{
public void
process(Element element);
}
The TagBean interface defines a process method that takes an element in from the DOM tree and performs some function against that element. The context of the entire DOM tree is available to the process method for manipulation through the DOM APIs. The routine begins at step 602 with the Java tag handler receiving the DOM element as input. At step 604, the handler then obtains the appropriate tagbean for the element as supplied by the taglib rules. A number of tagbean routines are illustrated in FIGS. 7-9 and will be described in more detailed below. At step 606, the handler extracts an attributeList from the element. The routine then performs a test at step 608 to determine whether there are any unprocessed attributes. If so, the routine branches to step 610 to determine whether the attribute maps to a setter property on the tagbean. If the outcome of the test at step 610 is negative, control returns to step 608. If, however, the outcome of the test at step 610 is positive, the routine continues at step 612 to set the value of the attribute on the tagbean. Control then continues at step 614 to remove the attribute from the attributeList. Processing then continues back at step 608. Thus, for each attribute in the attributeList, the handler checks for a corresponding setter property on the tagbean. If a corresponding setter property exists, the value of the attribute is set on the tagbean and the attribute is removed from the attribute list. When the outcome of the test at step 608 indicates that all attributes have been checked against the tagbean, routine branches to step 616. At this step, the tagbean's process method is called given the DOM element so that it can manipulate the tree in whatever manner it deems fit. When tagbean.process( ) is complete, the new document is returned from the tag handler at step 618. This completes the processing. FIGS. 7-9 illustrate tagbeans that are useful in the present invention. DOM In, Text Out Tagbean FIG. 7 illustrates a simple DOM in, Text out macro that has the following class:
public abstract class SimpleTagBean implements TagBean
{
public abstract String
translateElement(Element element);
public final void
process element element);
}
SimpleTagBean is a class created to simplify the task of writing a tagbean. Using this class, the developer merely has to implement the translateElement method, which takes in a DOM element and returns the corresponding text macro expansion. In particular, the routine reads the DOM tree (e.g., using the DOM APIs), produces an XML block (typically a scriplet), and uses the XML block to replace the current element in the tree. This is advantageous to the writer of the tagbean because, using the invention, he or she does not need to know how to create new nodes and to populate them with values. All the writer has to do is create an XML expanded form of the element passed in. While this approach requires increased execution time at translation, translation only happens once every time the page changes; thus, the technique has little impact on server performance. The SimpleTagBean class works as demonstrated in the flowchart of FIG. 7. The routine begins at step 702 with the Java tag handler calls SimpleTagBean.process with the appropriate tag element. At step 704, the SimpleTagBean hands the element off to its subclass's "overwritten" translateElementmethod. In the translateElement method, at step 706, the subclass looks at the element and its sub-elements and attributes to produce a text macro expansion of the node. The routine then continues at step 708 with the text expansion being returned to the SimpleTagBean.process method. At step 710, the XML is parsed backed into DOM. At step 712, the top node of the new document object replaces the element that was passed in from the previous document. In particular, in step 712, the top node of the new DOM replaces the element was passed into translateElement( ). This completes the processing. Text In, Text Out Tagbean FIG. 8 illustrates a Text in, Text out tagbean that may be used to isolate the developer from the DOM API. This is especially useful if the element contains only simple information or does simple query string replacement. A representative class is as follows:
public abstract class TextTagBean extends SimpleTagBean
{
public abstract String
translateText(String text);
public final String
translateElement(Element element);
public final void
process(Element element);
}
TextTagBean extends the SimpleTagBean functionality. In particular, the TextTagBean extends the SimpleTagBean class and implements the translateElement function to inherit the String V DOM output functionality. Instead of the developer writing translateElement, however, he or she now writes translateText. Referring now to FIG. 8, the routine begins at step 802 with the Java custom DOM tag handler handing the SimpleTagBean.process the appropriate element. At step 804, the routine hands the element off to the "overwritten" translateElement method. At step 806, the translateElement method converts the DOM directly into its corresponding XML. In particular, the TextTagBean.translateElement( ) takes the element and flattens it into XML without interpreting any of the XML. The routine then continues at step 808, with the XML then being passed to the translateText method of the subclass. At step 810, the translateText method reads the string and processes it to return a new XML string. In particular, translateText looks at the XML string and manipulates it to produce another text representation of it and returns this representation to TextTagBean.translateElement( ). At step 812, the new XML string is returned to the TextTagBean.translateElement, which returns the string to SimpleTagBean.process. SimpleTagBean.process finishes the processing at step 814, by turning the string into DOM and, at step 816, by replacing the previous element with the root of the new document. Thus, in step 816, the top node of the new DOM replaces the element that was passed into translateElemen to. This completes the processing. Multiple Scripting Language Blocks Another tagbean is illustrated in FIG. 9. This routine, called jsp:block, enables page developers to use multiple scripting languages in the same page. As will be seen, this enables people with different skill sets to add value to the same page. It also enables the developer to chose another language that might be more suitable for a specific job. The routine begins at step 902 with each jsp:block handed off to the JSPBlockTagBean. At step 904, the JSPBlockTagBean chooses the appropriate BlockTagBean according to the language attribute of the jsp:block element. At step 906, the language-specific BlockTagBean creates a methodDefinition element which, at step 908, is then filled with code to set up an appropriate runtime environment for the target language. At step 910, the methodDefinitionelement is inserted as a child of the root element in the document. The routine then continues at step 912 to create a methodCall element to replace the originaljsp:block element. DOM Tree Processing FIG. 10 illustrates a preferred routine for collapsing the DOM tree into the fewest possible methodCalls. The routine begins at step 1002 to test whether there are any unprocessed methodcalls in the document. If not, the routine is done. If, however, the outcome of the test at step 1002 is positive, the routine continues at step 1004 by setting a variable mc equal to the right-most unprocessed leaf node that is a method call. At step 1006, the routine sets a variable collapse equal to an attribute mc.getAttribute(collapse). At step 1008, the collapse attribute is checked. If this attribute is not true, control returns to step 1002. If the outcome of the test at step 1008 is positive, then the contents of the corresponding methodDefinition are expanded in place, and the methodDefinition and methodCalls are removed from the tree. In particular, the routine continues at step 1010 by setting a variablemd equal to the methodDefinition for the methodCall. At step 1012, a test is run to determine whether any child nodes exist in the methodDefinitionelement. If not, the routine branches to step 1014 to remove mc from the document, and control returns to step 1002. If, however, the outcome of the test at step 1012 is positive, the routine continues at step 1016 to let c equal the last child node in the methodDefinition. At step 1018, c is removed from the methodDefinition. The routine then continues at step 1020 to insert c before mc in the document. Control then returns back to step 1012. This completes the processing. For optimization purposes, it is desired to verify context between multiple related XML tags in a DOM. One or more of these related XML tags are custom tags within the context of the inventive framework. By way of brief background, when processing a single custom tag element, that element may need access to all other related tags, processed and unprocessed, within the DOM. Unfortunately, however, there may be other unprocessed custom tags in the DOM that, when processed, would result in one or more related tags the current element is interested in. One solution to this problem is to pass some state information from the current element through the page handling engine. A preferred technique, however, is to use the DOM itself to indicate state. Clean-up Processing FIG. 11 is a flowchart illustrating this clean-up processing. The routine begins at step 1102 during the processing of the DOM tree with a current element being processed replacing itself with a placeholder element. The placeholder element includes attributes indicating its state. At step 1104, a test is performed to determine if a clean-up element already exists for the element being processed. If not, the current element then creates a clean-up element at step 1106. At step 1108, this clean-up element is added to the DOM in a position where it will be processed after all elements related to the current element have been processed. Thus, for example, the clean-up element is added to the DOM as a child node to the root position. If the outcome of the test at step 1104 indicates that such a clean-up element already exists, the current element need not create another clean-up element; rather, the current element need only move the existing clean-up element later in the DOM to ensure it is processed after any other related elements might be processed. This is step 1110. When the processing routine finally encounters the clean-up element, as indicated by a positive outcome of the test at step 1112, this element scans the entire DOM for all the related tags (now placeholders) of interest. This is step 1114. At step 1116, the clean-up element loads the state information from each and, at step 1118, processes them accordingly. When complete, at step 1120, the clean-up element removes itself from the DOM. In this way, the technique shifts processing from each individual element to a single, last-processed element. Thus, in the preferred embodiment, a two-pass solution is implemented. In the first pass, simple translation is performed on the tag, creating new tag place holders to be handled by a clean-up phase. For example, assume the DOM includes the following tags: system:macro1, system:macro2, and system:macro3. It is also assumed that each relies on specific information from other tags but not all the information is available until all of them have been touched once. On the first pass, system:macro1 expands to _system_macro1 and performs all the metadata expansion it can perform at this time to assist the clean-up node. At this time, it also inserts a system:cleanup in the tree as the last child of jsp:root (assuming it is not already there). The second pass is triggered when the clean-up node is hit. For proper processing, it should check to make sure the first pass has completed (no system:macro1 or macro2 or macro3 tags in the tree). If other clean-up nodes exist in the tree, it should remove itself from the tree and let the other nodes handle the clean-up later. Once the clean-up node has determined that the tree is in the correct state, it goes through all the artifacts left by the first process and expands them with all the context available. The clean-up algorithm, which itself is novel, provides several advantages. It adds the capability of having dependencies between multiple custom tags. It further enables the scoping of custom tags within a page by a developer, which has not been available in the prior art. It further enables a tag developer to implement a given server page with a specific dependency-semantic, which is also unknown in the prior art. Tagbean Code Reduction Another optimization reduces the amount of code in the tagbeans. By way of background, if a developer expands everything necessary to perform a function of a tag, that process may produce large amounts of code. In particular, the writing of custom tagbeans may result in a large amount of Java code being generated into the resulting servlet. Because this code may be largely common across servlets generated from the same tagbean (variable names might change, but little else), according to the invention, the functionality is delegated to outside code as much as possible. Preferably, the code is factored into a separate Java bean, and the most convenient place to delegate is the very tagbean generating the code. Thus, the tagbean need only generate enough Java code for the servlet to call out to the separate bean. This dramatically reduces the code in the tag bean handler. As a result, this optimization improves maintainability and greatly simplifies debugging. In addition, because the code is not expanded, the function is hidden from anyone who has access to the generated servlet code. In addition, as a separate Java bean, developers are encouraged to put more error-handling code in the system that may not get put in otherwise. It also further stabilizes the system. Thus, in a preferred embodiment, instead of doing inline expansion of code, the developer may take runtime values of attributes and sub-elements and generate code to make them parameters of a method on the very same bean that can be called at runtime to do the real work. Thus, according to the invention, at translation time, a custom tag in the DOM tree is replaced, e.g., with a script that results in a line of code in a generated servlet. In that way, when the servlet is then executed at request time, the line of code invokes a method in a custom tagbean to perform a given function. A more detailed example of this optimization technique is set forth later in this disclosure. EXAMPLES As has been previously described, the flowchart of FIG. 2 illustrates the basic translation functionality of the present invention. An example of this operation is now provided. In the following example, mixed Languages.xsp is a flat input file, mixedLanguagesDOM1.txt is a representation of the DOM after the input file has been parsed and some metadata has been added to the DOM, mixedLanguagesDOM2.txt is a representation of the DOM after the custom tags in the input file have been processed (in this case, the <block> tag is a custom tag, with the inner tag processed before the outer tag, with the resulting DOM then ready to be walked by the servlet generator routine), and mixedLanguagesServlet.java is the servlet generated by walking the DOM that complies with the JSP 1.0 specification. mixedLanguages.xsp
<? xml version=1.0"?>
<jsp:root>
<jsp:block language="java">
<jsp:scriptlet>
String user = request.getParameter("user");
if(user == null) {
</jsp:scriptlet>
<b>No one is logged in.</b>
<jsp:block language="javascript">
<jsp:scriptlet>
var int x = 0;
x = x + 1;
</jsp:scriptlet>
</jsp:block>
<jsp:scriptlet>
}
else {
</jsp:scriptlet>
<b>Welcome:
<jsp:scriptlet>
out.println(user);
</jsp:scriptlet>
</b>
<jsp:scriptlet>
}
</jsp:scriptlet>
</jsp:block>
<jsp:root>
mixedLanguagesDOM1.txt
<jsp:root
servletPath="c:
.backslash.top.backslash.xsp.backslash.demo.backslash.test.backslash.mixed
LanguagesServlet.java"
servletPackageName="xsp.test.scripting" servletClassName=
"mixedLanguagesServlet">
<jsp:methodDefinition name="_jspService">
<jsp:block language="java">
<jsp:block language="java">
<jsp:scriptlet>
String user = request.getParameter("user");
if(user == null) {
<b>
No one is logged in.
<jsp:block language="javascript">
<jsp:scriptlet>
var int x = 0;
x = x + 1;
<jsp:scriptlet>
}
else {
<b>
Welcome:
<jsp:scriptlet>
out.println(user);
<jsp:scriptlet>
}
mixedLanguagesDOM2.txt
<jsp:root
servletPath="c:
.backslash.top.backslash.xsp.backslash.demo.backslash.test.backslash.mixed
LanguagesServlet.java"
servletPackageName="xsp.test.scripting"
servletClassName="mixedLanguageServlet">
<jsp:methodDefinition name="javascriptBlock1">
<jsp:scriptlet>
BSFManager bsfManager = new BSFManager( );
BSFEnvironment bsfEnvironment = new BSFEnvironment( );
bsfEnvironment.tempDir =
"c:.backslash..backslash.top.backslash..backslash.";
bsfEnvironment.classPath =
"c:.backslash..backslash.top.backslash..backslash.;c:
.backslash..backslash.prog.backslash..backslash.jdk118.backslash..backslas
h.lib.backslash..backslash.classes.zip;c:.backslash..backslash.top;c:
.backslash..backslash.prog.backslash..backslash.xm14j.backslash..backslash
.xm14j_1_1_16.jar;c:
.backslash..backslash.prog.backslash..backslash.lotusxsl_0
_17_0.backslash..backslash.lotusxsl.jar;c:
.backslash..backslash.prog.backslash..backslash.SQLLIB.backslash..backslas
h.java.backslash..backslash.db2java.zip;c:
.backslash..backslash.prog.backslash.SQLLIB.backslash..backslash.java.back
slash..backslash.runtime.zip;c:
.backslash..backslash.prog.backslash..backslash.
websphere.backslash..backslash.appserver.backslash..backslash.lib.backslash
..backslash.ibmwebas.jar;c:
.backslash..backslash.prog.backslash..backslash.websphere.backslash..backs
lash.appserver.backslash..backslash.lib.backslash..backslash.jsdk.jar;c:
.backslash..backslash.prog.backslash..backslash.websphe
re.backslash..backslash.appserver.backslash..backslash.lib.backslash..backs
lash.jst.jar;c:.backslash..backslash.top.backslash..backslash.bsf-1.
0b6.backslash..backslash.lib.backslash..backslash.bsf.jar;c:
.backslash..backslash.top.backslash..backslash.bsf-1.
0b6.backslash..backslash.lib.backslash..backslash.js.jar;c:
.backslash..backslash.top.backslash..backslash.bsf-1.
0b6.backslash..backslash.lib
.backslash..backslash.NetRexxC.zip;c:
.backslash..backslash.prog.backslash..backslash.websphere.backslash..backs
lash.appserver.backslash..backslash.lib.backslash..backslash.x509v1.jar;.;
c:
.backslash..backslash.prog.backslash..backslash.websphere.backslash..backs
lash.appserver.backslash..backslash.lib.backslash..backslash.ej
s.jar;c:.backslash..backslash.;c:
.backslash..backslash.prog.backslash..backslash.websphere.backslash..backs
lash.appserver.backslash..backslash.properties.backslash..backslash.ejs;C:
.backslash..backslash.prog.backslash..backslash.jdk116.backslash..backslas
h.bin.backslash..backslash....backslash..backslash.classes;C:
.backslash..backslash.prog.backslash..backslash.jdk1
16.backslash..backslash.bin.backslash..backslash...
.backslash..backslash.lib.backslash..backslash.classes.zip;C:
.backslash..backslash.prog.backslash..backslash.jdk116.backslash..backslas
h.bin.backslash..backslash...
.backslash..backslash.lib.backslash..backslash.classes.jar;C:
.backslash..backslash.prog.backslash..backslash.jdk116.backslash..backslas
h.bin.backslash..backslash...
.backslash..backslash.lib.backslash..backslash.rt.jar;C
:
.backslash..backslash.prog.backslash..backslash.jdk116.backslash..backslas
h.bin.backslash..backslash...
.backslash..backslash.lib.backslash..backslash.i18n.jar";
bsfEnvironment.classLoader = this.getClass( ).getClassLoader( );
bsfManager.setBSFEnvironment(bsfEnvironment);
BSFEngine javascriptInterpreter =
bsfManager.loadScriptingEngine("javascript");
javascriptInterpreter.setDebug(true);
bsfManager.registerBean("request",request);
bsfManager.registerBean("response", response);
bsfManager.registerBean("session", session);
bsfManager.registerBean("out", out);
bsfManager.registerBean("pageContext", pageContext);
bsfManager.registerBean("page", this);
try {
javascriptInterpreter.eval("var request =
bsf.lookupBean(.backslash."request.backslash.");.backslash.nvar response =
bsf.lookupBean(.backslash."response.backslash.");.backslash.nvar session =
bsf.lookupBean(.backslash."session.backslash.");.backslash.nvar out =
bsf.lookupBean(.backslash."out.backslash.");.backslash.nvar pageContext =
bsf.lookupBean(.backslash."pageContext.backslash.");.backslash.nvar page =
bsf.lookupBean(.backslash."page.backslash.");.backslash.n.backslash.n var
int x = 0;.backslash.n x = x + 1;.backslash.n ");
} catch (BSFException e) {
Throwable realException = e.getTargetException( );
while (realException instanceof BSFException) {
realException = ((BSFException)
realException).getTargetException( );
while (realException instanceof
java.lang.reflect.InvocationTargetException) {
realException=((java.lang.reflect.InvocationTargetException)
realException).getTargetException( );
}
}
realException.printStackTrace( );
throw new ServletException(realException.getMessage( ));
}
bsfManager.unregisterBean("request");
bsfManager.unregisterBean("response");
bsfManager.unregisterBean("session");
bsfManager.unregisterBean("out");
bsfManager.unregisterBean("config");
bsfManager.unregisterBean("pageContext");
bsfManager.unregisterBean("page");
<jsp:methodDefinition name="jspService">
<jsp:scriptlet>
String user = request.getParameter("user");
if (user == null) {
<b>
No one is logged in.
<jsp:methodCall name="_javascriptBlock1">
<jsp:scriptlet>
}
else {
<b>
Welcome:
<jsp:scriptlet>
out.println(user);
<jsp:scriptlet>
}
mixedLanguagesServlet.java
package xsp.test.scripting;
import org.w3c.dom.*;
import java.beans.*;
import com.lotus.xsl.xm14j2tx.*;
import java.io.*;
import com.ibm.bsf.*;
import java.util.*;
import com.sun.server.http.*;
import javax.servlet.jsp.*;
import com.lotus.xsl.*;
import javax.servlet.*;
import xsp.*;
import com.ibm.servlet.pagecompile.*;
import com.ibm.xml.parser.*;
import java.net.*;
import com.sun.server.util.*;
import java.lang.*;
import javax.servlet.http.*;
import com.ibm.servlet.engine.*;
public class mixedLanguagesServlet
extends xsp.ContractServlet
{
public void
_jspService(HttpServletRequestreq,
HttpServletResponseres)
throws ServletException, IOException
{
// Cast to HttpService objects for setAttribute/callPage
HttpServiceRequest request = (HttpServiceRequest)req;
HttpServiceResponseresponse =
new PCHttpServletResponseProxy((SEHttpServiceResponse)res);
JspWriter out = null;
// Create the JspFactory and obtain the PageContext
JspFactory factory = JspFactory.getDefaultFactory( );
PageContext pageContext =
factory.getPageContext(this, // JSP page
request, // Servlet request
response, // Servlet response
null, // Error page URL
true, // Servlet session
0, // JSP buffer size
true); // JSP autoflush
try {
// Initialize all the implicit variables
HttpSession session = request.getSession(true);
out = pageContext.getOut( );
Object page = this;
response.setContentType("text/html;charset=ISO-8859-1");
// Now generate fixed template and script code
String user = request.getParameter("user");
if (user == null) {
out.print("<b>");
out.println( );
out.println("No one is logged in.");
out.println("</b>");
javascriptBlock1(request,
response,
session,
out,
pageContext,
page);
}
else {
out.print("<b>");
out.println( );
out.println("Welcome:");
out.println(user);
out.println("</b>");
}
} finally {
out.close( );
((PCHttpServletResponseProxy)reponse).writeOutResponse( );
factory.releasePageContext(pageContext);
}
}
public void
javascriptBlock1(HttpServiceRequestrequest,
HttpServiceResponseresponse,
HttpSession session,
JspWriter out,
PageContext pageContext,
Object page)
throws ServletException, IOException
{
BSFManager bsfManager = new BSFManager( );
BSFEnvironment bsfEnvironment = new BSFEnvironment( );
bsfEnvironment.tempDir =
"c:.backslash..backslash.top.backslash..backslash.";
bsfEnvironment.classPath =
"c:.backslash..backslash.top.backslash..backslash.;c:
.backslash..backslash.prog.backslash..backslash.jdk118.backslash..backslas
h.lib.backslash..backslash.classes.zip;c:.backslash..backslash.top;c:
.backslash..backslash.prog.backslash..backslash.xm14j.backslash..backslash
.xm14j_1_1_16.jar;c:
.backslash..backslash.prog.backslash..backslash.lotusxsl_0
_17_0.backslash..backslash.lotusxsl.jar;c:
.backslash..backslash.prog.backslash..backslash.SQLLIB.backslash..backslas
h.java.backslash..backslash.db2java.zip;c:
.backslash..backslash.prog.backslash..backslash.SQLLIB.backslash..backslas
h.java.backslash..backslash.runtime.zip;c:
.backslash..backslash.prog.backslash..backslash.
websphere.backslash..backslash.appserver.backslash..backslash.lib.backslash
..backslash.ibmwebas.jar;c:
.backslash..backslash.prog.backslash..backslash.websphere.backslash..backs
lash.appserver.backslash..backslash.lib.backslash..backslash.jsdk.jar;c:
.backslash..backslash.prog.backslash..backslash.websphe
re.backslash..backslash.appserver.backslash..backslash.lib.backslash..backs
lash.jst.jar;c:.backslash..backslash.top.backslash..backslash.bsf-1.
0b6.backslash..backslash.lib.backslash..backslash.bsf.jar;c:
.backslash..backslash.top.backslash..backslash.bsf-1.
0b6.backslash..backslash.lib.backslash..backslash.js.jar;c:
.backslash..backslash.top.backslash..backslash.bsf-1.
0b6.backslash..backslash.lib
.backslash..backslash.NetRexxC.zip;c:
.backslash..backslash.prog.backslash..backslash.websphere.backslash..backs
lash.appserver.backslash..backslash.lib.backslash..backslash.x509v1.jar;.;
c:
.backslash..backslash.prog.backslash..backslash.websphere.backslash..backs
lash.appserver.backslash..backslash.lib.backslash..backslash.ej
s.jar;c:.backslash..backslash.;c:
.backslash..backslash.prog.backslash..backslash.websphere.backslash..backs
lash.appserver.backslash..backslash.properties.backslash..backslash.ejs;C:
.backslash..backslash.prog.backslash..backslash.jdk116.backslash..backslas
h.bin.backslash..backslash....backslash..backslash.classes;C:
.backslash..backslash.prog.backslash..backslash.jdk1
16.backslash..backslash.bin.backslash..backslash...
.backslash..backslash.lib.backslash..backslash.classes.zip;C:
.backslash..backslash.prog.backslash..backslash.jdk116.backslash..backslas
h.bin.backslash..backslash...
.backslash..backslash.lib.backslash..backslash.classes.jar;C:
.backslash..backslash.prog.backslash..backslash.jdk116.backslash..backslas
h.bin.backslash..backslash...
.backslash..backslash.lib.backslash..backslash.rt.jar;C
:
.backslash..backslash.prog.backslash..backslash.jdk116.backslash..backslas
h.bin.backslash..backslash...
.backslash..backslash.lib.backslash..backslash.i18n.jar";
bsfEnvironment.classLoader = this.getClass( ).getClassLoader( );
bsfManager.setBSFEnvironment(bsfEnvironment);
BSFEngine javascriptInterpreter =
bsfManager.loadScriptingEngine("javascript");
javascriptInterpreter.setDebug(true);
bsfManager.registerBean("request", request);
bsfManager.registerBean("response", response);
bsfManager.registerBean("session", session);
bsfManager.registerBean("out", out);
bsfManager.registerBean("pageContext", pageContext);
bsfManager.registerBean("page", this);
try {
javascriptInterpreter.eval("var request =
bsf.lookupBean(.backslash."request.backslash.");.backslash.nvar response =
bsf.lookupBean(.backslash."response.backslash.");.backslash.nvar session =
bsf.lookupBean(.backslash."session.backslash.");.backslash.nvar out =
bsf.lookupBean(.backslash."out.backslash.");.backslash.nvar pageContext =
bsf.lookupBean(.backslash."pageContext.backslash.");.backslash.nvar page =
bsf.lookupBean(.backslash."page.backslash.");.backslash.n.backslash.n var
int x = 0;.backslash.n x = x + 1;.backslash.n ");
} catch (BSFException e) {
Throwable realException = e.getTargetException( );
while (realException instanceof BSFException) {
realException = ((BSFException)
realException).getTargetException( );
while (realException instanceof
java.lang.reflect.InvocationTargetException) {
realException = ((java.lang.reflect.InvocationTargetException)
realException).getTargetException( );
}
}
realException.printStackTrace( );
throw new ServletException(realException.getMessage( ))
}
bsfManager.unregisterBean("request");
bsfManager.unregisterBean("response");
bsfManager.unregisterBean("session");
bsfManager.unregisterBean("out");
bsfManager.unregisterBean("config");
bsfManager.unregisterBean("pageContext");
bsfManager.unregisterBean("page");
}
}
The following illustrates more generally how an input file is translated into a DOM data structure. In particular, for a given input file:
<a>
<b>
<c/>
<d/>
</b>
<e>
<f/>
<g/>
<h/>
</e>
</a>
the DOM data structure would look as follows: ##STR1## Thus, in this example, node a is a parent node that has child nodes b and e. Node b has child nodes c and d, and node e has child nodes f and h. Node f has child node g. In the preferred embodiment, the order of node execution would then be as follows: c, d, b, g, f, h, e and a. The value of executing the nodes in an inside-out fashion is that the innermost tagbean can replace itself with a JSP syntax element that is well known to an outer tagbean so that the outer tagbean knows how to process the internal element. For example, in the case of the multi-language support, like:
<block language="java">
<jsp:scriplet>
javaExpression;
javaExpression2;
</jsp:scriptlet>
<block language="javascript">
<jsp:scriptlet>
javascriptExpression;
javascriptExpression2;
<jsp:scriptlet>
</block>
</block>
The block tag is a custom tag. When this tag executes, it transforms everything inside it into Java code. The invention is able to transform the contained custom tag because the innermost custom tag preferably is handled first, and such processing leaves behind a well known tag that the outer most custom tag handler knows how to transform. The following code illustrates how scripting language blocks may be used to support multiple scripting languages in a single web page. As described above, nesting of different scripting languages is supporting by marking where one section, or "block", of code begins and where it ends. For example, the following snippet has JavaScript code nested within REXX code nested within Java code:
if(true) {
say I am true
if also Tue then do
vari = 5;
end
System.out.println(bean.getProperty( ));
}
If this code were to appear in a web page, the blocks of code may be marked as follows:
<BLOCK language="java">
if(true) {
<BLOCK language="netrexx">
say I am true
if also True then do
<BLOCK language="javascript">
var i = 5;
</BLOCK>
end
</BLOCK>
System.out.println(bean.getProperty( ));
}
</BLOCK>
In the above example, it can be seen that "end" is associated with "if also True then do" and not, for example, with "var i=5." This enables the code to correctly process all the languages at runtime. In the above example, it should be noted that the blocks are nested. This is not a limitation. Indeed, there is no reason they cannot be peers, as below:
<BLOCK language="java">
// a little Java code here
</BLOCK>
<BLOCK language="javascript">
/* some JavaScript code here */
</BLOCK>.
As described, the implementation compiles a web page into a XML (extensible Markup Language) DOM (Document Object Model), and from there, into a Java servlet. In the DOM stage, the routine looks for BLOCK nodes. When encountering one, the routine creates a new node representing a Java method definition as a child of the root element, and replaces the BLOCK node with a node representing a Java method call to the new method definition. The block's child nodes are then moved under the method definition. Javaservlet code is then generated under the method definition to pass the script code contained in the block to an appropriate interpreter for the scripting language specified by the block's "language" attribute. The same operation is done for nested blocks. The innermost block is turned into a method definition and replaced by the method call node. When the next outer block is processed into its method definition, the block must turn any method call nodes among its children into valid method calls into the servlet written in the outer block's language. In the nested example above, the resulting Javaservlet might then contain code as follows:
protected void
javBlock0(args)
{
if(true) {
netrexxBlock0(args);
System.out,println(bean.getProperty( ));
}
}
protected void
javascriptBlock0(args)
{
javascriptInterpreter.process("var i = 5;");
}
protected void
netrexxBlock0(args)
{
netrexxInterpreter.process("say I am true .backslash.n if also True
then do .backslash.n
thisServlet.javascriptBlock0(args).backslash.n end");
}
The bolded text above represents the method call node transformed into a valid method call in the particular scripting language. Because in the preferred embodiment the runtime is written in Java, a special interpreter is not required to handle the javaBlock0 script code. The following illustrates how the invention verifies context between multiple related XML tags. This example starts with a sample input XML chunk and ends with a code chunk for use in the final servlet:
(1) xml
<trace:sink file="/myTrace.out">
<trace:output>foo + bar</trace:output>
</trace:sink>
(2) trace:output is handled by TagBean
- creates a marker with all currently known state
- creates a trace:cleanup-markers tag which will signal the 2nd pass
<trace:cleanup-markers/>
<trace:sink file="/myTrace.out">
<_trace_output_marker>foo + bar</_trace_output_marker>
</trace:sink>
(3) trace:sink is handled
- produces scriptlets to handle body of the work
- adds metadata to_trace_output_marker
<trace:cleanup-markers/>
<jsp:scriptlet>
try
FileWriterfileWriter= new FileWriter("/myTrace.out");
PrintWriter printWriter = new PrintWriter(fileWriter);
</jsp:scriptlet>
<_trace_output_marker output="printWriter">foo +
bar</_trace_output_marker>
<jsp:scriptlet>
} finally {
printWriter.flush( );
fileWriter.flush( );
printWriter.close( );
fileWriter.close( );
}
</jsp:scriptlet>
(4) trace:cleanup-markers is handled
- replaces_trace_output_marker with a jsp:scriptlet
<jsp:scriptlet>
try
FileWriter fileWriter = new FileWriter("/myTrace.out");
PrintWriter printWriter = new PrintWriter(fileWriter);
</jsp:scriptlet>
<jsp:scriptlet>
printWriter.println(foo + bar);
</jsp:scriptlet>
<jsp:scriptlet>
} finally {
printWriter.flush( );
fileWriter.flush( );
printWriter.close( );
fileWriter.close( );
}
</jsp:scriptlet>
(5) final translation step ofjsp:scriptlet to Java code
try
FileWriter fileWriter = new FileWriter("/myTrace.out");
PrintWriter printWriter = new PrintWriter(fileWriter);
printWriter.println(foo + bar);
} finally {
printWriter.flush( );
fileWriter.flush( );
printWriter.close( );
fileWriter.close( );
}
As also noted above, the present invention provides a technique for reducing the amount of code in the tagbeans. An example of this optimization technique is now provided. The following ServletTagBean j is the original code file:
{
protected String name = null;
protected String code = null;
protected String codebase = null;
public void
setName(String name)
{
this.name = name;
}
public void
setCode(String code)
{
this.code = code;
}
public void
setCodebase(String codebase)
{
this.codebase = codebase;
}
public String
translateElement(Element element)
{
Hashtable initParams = parseInitParams(element);
// For each param sub element, add the name/value to a map for later
Hashtable paramMap = Utility.parseParams(element);
// The name or code parameter must be set.
if (name == null && code == null) {
// Error!!!
System.out.println("Error: name and code can not be null the same
time");
return null;
}
StringBuffer buff = new StringBuffer( );
buff.append(".backslash.n")
.append("<")
.append(SCRIPTLETTAG)
.append(">.backslash.n.backslash.n")
.append("try {.backslash.n")
.append(" String _code = null;.backslash.n");
if (name == null) {
buff.append(" String _name = null;.backslash.n");
}
else {
buff.append(" String _name = .backslash."")
.append(name)
.append(".backslash.";.backslash.n");
}
if (code != null) {
buff.append(" _code = .backslash."")
.append(code)
.append(".backslash.";.backslash.n.backslash.n");
}
buff.append(".backslash.n if (_name == null .parallel.
_name.equals(.backslash.".backslash.")) {.backslash.n")
.append(" _name = _code;.backslash.n")
.append(" }.backslash.n.backslash.n")
.append(" Servlet_s = .backslash.n")
.append(" getServletConfig( ).getServletContext( ).")
.append("getServlet(_name);.backslash.n")
.append(" if (_s == null) {.backslash.n")
.append(" Properties _init = new Properties( );.backslash.n")
.append(" _init.put(.backslash."name.backslash.", .backslash."")
.append(name)
.append(".backslash.");.backslash.n");
if (code != null) {
buff.append(" _init.put(.backslash."code.backslash.", .backslash."")
.append(code)
.append(".backslash.");.backslash.n");
}
if (initParams.size( ) > 0) {
Enumeration e = initParams.keys( );
while (e.hasMoreElements( )) {
String key = (String) e.nextElement( );
String value = (String) initParams.get(key);
buff.append(" _init.put(.backslash."")
.append(key)
.append(".backslash.", .backslash."")
.append(value)
.append(".backslash.");.backslash.n");
}
}
if (codebase != null) {
buff.append(" _init.put(.backslash."codebase.backslash.",
.backslash."")
.append(codebase)
.append(".backslash.");.backslash.n")
.append(" _s = ")
.append("com.sun.server.http.pagecompile..backslash.n")
.append(".backslash.tServletUtil.loadServlet(this,")
.
append(".backslash.n.backslash.t.backslash.t.backslash.t.backslash.t_name,
.backslash.n.backslash.t.backslash.t.backslash.t.backslash.t_code,")
.append("
.backslash.n.backslash.t.backslash.t.backslash.t.backslash.t.backslash."")
.append(codebase)
.append(".backslash.",
.backslash.n.backslash.t.backslash.t.backslash.t.backslash.t_init);
.backslash.n");
}
if (codebase == null) {
buff.append(" _s = ")
.append("com.sun.server.http.pagecompile.
.backslash.n.backslash.tServletUtil.")
.append("loadServlet(this,")
.
append(".backslash.n.backslash.t.backslash.t.backslash.t.backslash.t_name,
")
.append("
.backslash.n.backslash.t.backslash.t.backslash.t.backslash.t_code,.backsla
sh.n.backslash.t.backslash.t.backslash.t.backslash.tnull,")
.
append(".backslash.n.backslash.t.backslash.t.backslash.t.backslash.t_init)
;.backslash.n");
}
buff.append(" }.backslash.n.backslash.n");
if (paramMap.size( ) > 0) {
buff.append(" java.util.Hashtable _parm = new ")
.append(" java.util.Hashtable( );.backslash.n");
Enumeration e = paramMap.keys( );
buff.append(" String[ ] _vals; .backslash.n");
while (e.hasMoreElements( )) {
String key = (String) e.nextElement( );
String value = (String) paramMap.get(key);
buff.append(" _vals = new String[1];.backslash.n")
.append(" _vals[0] = .backslash."")
.append(value)
.append(".backslash.";.backslash.n")
.append(" _parm.put(.backslash."")
.append(key)
.append(".backslash.", _vals);.backslash.n");
}
}
buff.append(".backslash.n out.flush( );");
buff.append(".backslash.n if (_s != null) {.backslash.n");
if (paramMap.isEmpty( )) {
buff.append(" com.sun.server.http.pagecompile.")
.append("ServletUtil.")
.append("callServlet(_s, _name, request,
response);.backslash.n");
} else {
buff.append(" HttpServletRequest_r;.backslash.n")
.append(".backslash.n _r = new .backslash.n
com.sun.server.http.")
.append("pagecompile.ParamsHttpServletRequest(")
.
append("request,.backslash.n.backslash.t.backslash.t.backslash.t.backslash
.t.backslash.t.backslash.t.backslash.t _parm);.backslash.n")
.append(" com.sun.server.http.pagecompile.")
.append("ServletUtil.callServlet(_s,")
.
append(".backslash.n.backslash.t.backslash.t.backslash.t.backslash.t.backs
lash.t.backslash.t.backslash.t _name,")
.
append(".backslash.n.backslash.t.backslash.t.backslash.t.backslash.t.backs
lash.t.backslash.t.backslash.t _r,")
.
append(".backslash.n.backslash.t.backslash.t.backslash.t.backslash.t.backs
lash.t.backslash.t.backslash.t response);.backslash.n");
}
buff.append(" }.backslash.n")
.append("} catch(Exception e) {.backslash.n")
.append(" e.printStackTrace( ); .backslash.n")
.append(" throw new ServletException(.backslash."Exception")
.append("caught for servlet: .backslash." +.backslash.n
e.getMessage( ));.backslash.n")
.append("}.backslash.n")
.append(".backslash.n</")
.append(SCRIPTLETTAG)
.append(">");
return buff.toString( );
}
public Hashtable
parseInitParams(Element element)
{
Hashtable initParams = new Hashtable( );
NamedNodeMap namedNodeMap = element.getAttributes( );
int attributeLength = namedNodeMap.getLength( );
Node attributeNode = null;
String nodeName = null;
String nodeValue = null;
for (int i = 0; i < attributeLength; i++) {
attributeNode = namedNodeMap.item(i);
nodeName = attributeNode.getNodeName( );
nodeValue = attributeNode.getNodeValue( );
// System.out.println("nodeName==" + nodeName +
// " nodeValue==" + nodeValue);
if (!(nodeName.equals("type")) &&
!(nodeName.equals("Name")) &&
!(nodeName.equals("Code")) &&
!(nodeName.equals("Codebase"))){
initParams.put(nodeName, nodeValue);
}
}
return initParams;
}
}
The following class, ServletTagBeanjava, is the recoded class using the delegation model of the invention. package xsp.was; import xsp.*; import java.io.*; import java.util.*; import org.w3c.dom.*; import javax.servlet.*; import javax.servlet.http.*; import javax.servletjsp.*; import com.sun.server.http.pagecompile.*; public class ServletTagBean extends SimpleTagBean
{
protected static int count = 0;
public String
translateElement(Element element)
{
Properties initParameters = parseInitParams(element);
Hashtable runtimeParameters = Utility.parseParams(element);
String name = (String) initParameters.get("name");
String code = (String) initParameters.get("code");
String codebase = (String) initParameters.get("codebase");
// The name or code parameter must be set.
if ((name == null) &&
(code == null)) {
System.out.println("Error: name and code can not be null
the same time");
return null;
}
int currentCount = ++count;
String initParametersName =
"_xsp_servletTagBean_initParameters" + currentCount;
String runtimeParametersName =
"_xsp_servletTagBean_runtimeParameters" + currentCount;
String output =
INDENT2 + "<jsp:scriptlet>.backslash.n" +
INDENT2 + "Properties" +
initParametersName + "=" +
"new Properties( );.backslash.n" +
INDENT2 + "Properties" + runtimeParametersName +
" = new Properties( );.backslash.n";
Enumeration enumeration = initParameters.keys( );
while (enumeration.hasMoreElements( )) {
String key = (String) enumeration.nextElement( );
String value = (String) initParameters.get(key);
output +=
INDENT2 + initParametersName + ".put(" +
stringify(key) +"," +
stringify(value) + ");";
}
enumeration = runtimeParameters.keys( );
while (enumeration.hasMoreElements( )) {
String key = (String) enumeration.nextElement( );
String value = (String) runtimeParameters.get(key);
output +=
INDENT2 + runtimeParametersName + ".put(" +
stringify(key) + "," +
stringify(value) + ");";
}
output +=
INDENT2 + "xsp.was.ServletTagBean.runServlet(this, .backslash.n" +
INDENT3 + "request, .backslash.n" +
INDENT3 + "response, .backslash.n" +
INDENT3 + initParametersName + ",.backslash.n" +
INDENT3 + runtimeParametersName +");";
output +=
INDENT2 + "</jsp:scriptlet>.backslash.n";
return output;
}
protected static final String
normalize(String input)
{
if (input == null) {
return null;
}
input = input.trim( );
if (input.equals("")) {
return null;
}
return input;
}
protected static final String
stringify(String input)
{
if (input == null) {
return "null";
}
return ".backslash."" + input +".backslash."";
}
public static void
runServlet(Servlet containingServlet,
HttpServletRequest request,
HttpServletResponse response,
Properties initParameters,
Properties runtimeParameters)
throws ServletException, IOException
{
String name = normalize((String) initParameters.get("name"));
String code = normalize((String) initParameters.get("code"));
String codebase = normalize((String) initParameters.get("codebase"));
if ((name == null) &&
(code == null)) {
throw new IllegalStateException("name or code must
be non-null");
}
if (name == null) {
name = code;
}
initParameters.put("name", name);
if (code != null) {
initParameters.put("code", code);
}
Servlet servlet = ServletUtil.loadServlet(containingServlet,
name,
code,
codebase,
initParameters);
if (!runtimeParameters.isEmpty( )) {
request = new ParamsHttpServletRequest(request,
runtimeParameters);
}
ServletUtil.callServlet(servlet, name, request, response);
}
public Properties
parseInitParams(Element element)
{
Properties initParams = new Properties( );
NamedNodeMap namedNodeMap = element.getAttributes( );
int attributeLength = nameNodeMap.getLength( );
Node attributeNode = null;
String nodeName = null;
String nodeValue = null;
for (int i = 0; i < attributeLength; i++) {
attributeNode = namedNodeMap.item(i);
nodeName = attributeNode.getNodeName( );
nodeValue = attributeNode.getNodeValue( );
initParams.put(normalize(nodeName),
normalize(nodeValue));
}
return initParams;
}
}
The developer need not write code generation code to produce code that will be robust for every possible input scenario. Instead, the developer need only write the code once, and the only code generation is used to delegate to the method that is written once. So, to provide a generic example:
1. String output = "out.write(.backslash."" + string +".backslash.");";
becomes:
2. String output = "PrintTagBean.print(out, .backslash."" + string +
".backslash.");";
The out.write( ) is moved into a method print( ) on PrintTagBean:
public static void
print(Writer out,
String string)
{
out.write(string);
}
As can be seen, in the first case, the code relies upon a variable `out` that exists in the servlet. The write( ) method was called on `out` passing it a string. Thus, to perform proper delegation, a method on PrintTagBean is created that takes `out` and the `string` and calls "out.write(string)". Thus, according to the invention, at translation time, a custom tag in the DOM tree is replaced, e.g., with a script that results in a line of code in a generated servlet. In this way, when the servlet is then executed at request time, the line of code invokes a method in a custom tagbean to perform a given function. If the code generated to handle runtime requests is longer than the code generated to pass the necessary variables to a method to be processed, there are several benefits to this approach. First, writing code to generate code is a very tedious and error-prone task; thus, reducing this code even slightly reduces the numbers of errors drastically. Second, using this approach, all the code handling of a task is actually handled in a single method that can be carefully crafted to handle correct inputs to produce the right output. Because this code is in the tagbean, it can be compiled immediately and checked for language syntax errors. If, instead, the code is generated each time, it will not be compiled until an XSP is written to test the functionality. Moreover, with branching (if statements) in code generation, it may take several tests just to test the syntax of all the possible code generations. Further, if the developer needs to change the function and "optimization" has already taken place, then the developer need only update a single method. Otherwise, the developer must go through the process of updating all the code generating code. Because of this reduction in code and code complexity, the maintenance of the code will be much lower. The present invention provides numerous other advantages over the prior art. In effect, the inventive page handling mechanism combines the manipulation and template mechanism of XSLT with the scripting capabilities of the JSP/ASP model. In addition, the invention provides a framework for enabling any programming language to be plugged into that model. Further, given that most languages are easily defined in Java byte code, the invention is economical to implement in a runtime using, for example, a Java Virtual Machine. The present invention uses custom DOM tags together with a framework and runtime that provides a powerful macro language to XML/JSP. The custom DOM tags allow a web page author the ability to define a simple markup language tag, e.g., <SHOPPING_CART>, that, at page translation time, is converted into script code by a generic Java object or an XSL stylesheet. This script code is then compiled into Java code and then into a Java servlet, yielding excellent performance servicing a client's request. Because the custom tag replaces the script code in the authored page, the page is kept clean and easy to maintain. The script code is kept separate and, thus, need only be debugged once. Normal ASP development, on the contrary, would force this code to remain in the page, and it would have to be debugged after every modification. The inventive framework is quite advantageous in that it is built on top of XML. Moreover, one of ordinary skill will appreciate that the framework is defineable programmatically or with XSL. In addition, macros written according to the invention can affect the output of an entire page and not just the content between a given pair of tags. The invention also enables one or more web page authors to support multiple scripting languages in a single web page. Further, in a preferred embodiment, the context of multiple related XML tags in a DOM may be verified by using the DOM itself to indicate state information. As noted above, the inventive mechanism is preferably implemented in or as an adjunct to a web server. Thus, the invention does not require any modifications to conventional client hardware or software. Generalizing, the above-described functionality is implemented in software executable in a processor, namely, as a set of instructions (program code) in a code module resident in the random access memory of the computer. Until required by the computer, the set of instructions may be stored in another computer memory, for example, in a hard disk drive, or in a removable memory such as an optical disk (for eventual use in a CD ROM) or floppy disk (for eventual use in a floppy disk drive), or downloaded via the Internet or other computer network. In addition, although the various methods described are conveniently implemented in a general purpose computer selectively activated or reconfigured by software, one of ordinary skill in the art would also recognize that such methods may be carried out in hardware, in firmware, or in more specialized apparatus constructed to perform the required method steps. Further, as used herein, a Web "client" should be broadly construed to mean any computer or component thereof directly or indirectly connected or connectable in any known or later-developed manner to a computer network, such as the Internet. The term Web "server" should also be broadly construed to mean a computer, computer platform, an adjunct to a computer or platform, or any component thereof. Of course, a "client" should be broadly construed to mean one who requests or gets the file, and "server" is the entity which downloads the file. Having thus described my invention, what I claim as new and desire to secure by Letters Patent is set forth in the following claims:
|
Same subclass Same class Consider this |
||||||||||
