J2EE & Web Services

Building XML-Enabled JavaServer Faces Applications
By Yuli Vasiliev

Learn how to build a JavaServer Faces application that interacts with Oracle XML DB.

Downloads for this article:
Sample code and listings (ZIP)
Oracle JDeveloper 10g 10.1.3 or later
Oracle Database 10g
Oracle Application Server Containers for J2EE (OC4J) 10g 10.1.2


JavaServer Faces (JSF) technology is a new server-side user interface (UI) component framework that is quickly becoming the standard Web-application framework for J2EE applications. The big advantage of JSF technology is that it enables Web developers to apply the Model-View-Controller (MVC) principles, thus achieving a clean separation between the model and presentation layers of a Web application. Practically, such a separation allows developers to take advantage of the division of labor—Java developers can concentrate on the application back-end and integration code, and page authors, who need not have any programming expertise, can focus on designing the Web pages of the application, laying out the UI components on the pages and wiring them to the application back-end objects. Obviously, this approach makes it easier to develop more-flexible, -scalable, and -maintainable solutions. For example, adding XML support to an existing JSF application most likely will not require making any changes to the JSF pages of that application. Instead, only the application back-end needs to be revised.

While the best way to learn new technologies is to use them to solve a real problem, in this article I will explain how to develop a simple JSF application that works on the idea of XML and interacts with Oracle XML DB, a feature of Oracle Database that enables high-performance native storage and retrieval of XML content. Proceeding to the sample that accompanies this article, you will start by developing a simple newsletter application that is based on the JSF framework and originally doesn't provide any XML support. Once you have a non-XML-enabled JSF application, I'll focus on how to add XML functionality to it. The main goal here is to demonstrate different ways in which you can add XML functionality to an existing JSF application.

Requirements

As a JSF application is essentially a standard Java Web application running on a Web container that must support Servlet 2.3 and JSP 1.2 (or higher), this article assumes that you already have an installed version of a Web container, such as Oracle Application Server Containers for J2EE (OC4J) 10g 10.1.2 or Tomcat 5.0. Another software package required to develop JSF applications is the JSF Reference implementation. Fortunately, Oracle JDeveloper, starting with release 10.1.3, comes with built-in support for JSF. At the time of this writing, Oracle JDeveloper 10.1.3 Developer Preview is available for download. This preview not only includes the JSF Reference implementation bundled with JDeveloper IDE but also provides the developer with a rich set of tools designed to simplify development of JSF applications. These tools include a JSF-enabled JSP Visual Editor that renders the JSF components in the visual editor and a JSF page flow diagrammer that allows you to visually design a JSF navigation model.

Developing JSF Applications with JDeveloper

In spite of the fact that you can produce all the JSF pages, Java source code, and configuration files for the sample application by using a simple text editor, such as Notepad or Vi, and then compile the Java sources from a command-line prompt by using the javac compiler, this article assumes that you will use Oracle JDeveloper 10g release 10.1.3 or later to handle all these tasks. Generally, there are a lot of reasons why you might want to choose JDeveloper 10g as your development tool for building a JSF application that interacts with the database. First of all, using JDeveloper 10g will allow you to take advantage of a wide range of tools that can be used for developing, debugging, testing, tuning, and deploying your JSF application. Moreover, JDeveloper 10g is optimized to assist developers in producing a multitier architecture for database applications. The latter is especially useful when you're developing complex JSF applications—recall that one of the key advantages of using JSF technology is the ability to organize Web applications along the MVC architecture, which allows developers to separate the presentation from the business logic of an application.

Note, however, that the focus of this article is not on how to use JSF with JDeveloper. (Many other resources are available for that purpose.) Clearly, using JDeveloper can help you simplify and speed up the development process for building the sample application that accompanies the article. Nevertheless, to build the sample, you can still do without JDeveloper.

Building a Simple JSF Application Without XML Support

This section walks you step by step through the process of building a JSF application organized along the MVC architecture. When you've completed the section, you will have a simple newsletter application (actually, it doesn't send the newsletters but lets the user subscribe) that originally doesn't provide XML functionality. The next sections will show you how to add XML functionality to that JSF application.

Developing model beans. This subsection focuses on developing the model objects, whose purpose is to handle application data, including moving data in and out of the database. In particular, you'll see how to create the model beans that are meant to hold and manipulate the login and subscriber information, respectively. However, as you will learn in the next subsections, the application doesn't use these model objects directly. Instead, it uses the LoginBean and SubscriberBean backing beans that extend the Login and Subscriber model beans, respectively, allowing for greater separation of the model layer from the presentation layer. The LoginBean and SubscriberBean backing beans are discussed in the “Developing Backing Beans” subsection later in this section.

Now that you have an idea of what you will be building, let's get started. If you're using Oracle JDeveloper, you start by creating a new application. Name the application jsfsubscr, specify the same name for Application Package Prefix, and leave Directory Name at its default setting. For Application Template, choose Web Application [JSF, JSP, EJB] (this template is available in Oracle JDeveloper starting with release 10.1.3). As a result, JDeveloper will create the jsfsubscr application that consists of two projects, namely Model and ViewController. You use the Model project for the model objects that will contain the business logic for the application. In contrast, the ViewController project is for the JSP pages, configuration files, backing beans, and other components that make up the presentation layer and define application behavior.

Before you proceed with the jsfsubscr application, make sure to specify that the ViewController project depends upon the Model project. To do this, open the Project Properties\Dependencies dialog for the ViewController project and click the Model.jpr checkbox.

You start out with a Login class in the Model project. This model bean is intended to hold the user credentials:

package jsfsubscr.model;

public class Login {

private String email;
private String password;

public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}

public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

To hold the information submitted by a subscriber, the application will use a Subscriber class that extends Login:

package jsfsubscr.model;

public class Subscriber extends Login {

private String name;
private String phone;
private boolean subscribed;

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}

public boolean isSubscribed() {
return subscribed;
}
public void setSubscribed(boolean subscribed) {
this.subscribed = subscribed;
}

}

Accessing the database. Another important business object used in the sample application is the DBUtils data access object, which provides methods for accessing and manipulating the application data stored in the database. You also create this class in the Model project. Of course, you could add the database code directly into the model beans discussed above. However, it is always a good idea to have a separate layer for business objects intended to communicate with a database. Consider the DBUtils class shown in Listing 1 (in sample code download).

Note that each public method of the DBUtil class uses the throws clause to declare the exception that the method explicitly throws using the Java throw statement. If an exception occurs, the method simply rethrows it to the next-higher-level exception handler defined in the calling code. A new exception is created with the original cause attached, thus allowing you to rethrow the exception to the calling code without losing the original cause of the exception. Since the methods of DBUtil simply rethrow exceptions, taking no further action, you might want to know where the rethrown exceptions are handled. This will become clear when you start developing the application backing beans discussed in the “Developing Backing Beans” subsection. In particular, you'll learn that UI-specific code needs to know the details of the failure in order to return an appropriate result string to the navigation handler.

Looking through the code shown in Listing 1, you may notice that all the SQL statements used there query the only database table, namely subscriber. Make sure to create the subscriber table in the database as follows:

CREATE TABLE subscriber (
email VARCHAR2(50) PRIMARY KEY,
password VARCHAR2(30),
name VARCHAR2(30),
phone VARCHAR2(15),
subscribed NUMBER(1)
)

Before you begin to use the DBUtils class, you have to create a getDBUtils public method that will return an instance of that class to the code that uses the class methods. Actually, you might create the getDBUtils method within the DBUtils class itself, as well as declare a private static property there to hold an instance of the class. Alternatively, you might put the getDBUtils method in a separate Utils class in the Model project, as shown below:

package jsfsubscr.model;

public class Utils {

private static DBUtils dbUtils;

public synchronized static DBUtils getDBUtils() throws Exception{
if (dbUtils == null)
try {
dbUtils = (DBUtils)Class.forName("jsfsubscr.model.DBUtils").newInstance();
}
catch (Exception e) {
throw new Exception (e.getMessage());
}
return dbUtils;
}
}

Now that you have created a data access object that provides the necessary methods for accessing and manipulating data stored in the database and defined a method that will return an instance of that data access object, you can turn back to the model objects discussed earlier and enhance their functionality by adding the methods that will operate on the data. You start by adding a doLogin method to the Login model bean. Consider the following doLogin method:

public void doLogin() throws Exception {
try {
subscriber = Utils.getDBUtils().select(this.email);
if(!password.equals(subscriber.getPassword().trim()))
throw new Exception("Wrong Password");
}
catch (Exception e) {
throw new Exception(e.toString());
}
}

Also, make sure to add the subscriber property to the Login class:

private Subscriber subscriber;

Next, create the get accessor to allow the calling code to access the subscriber property:

public Subscriber getSubscriber() {
return subscriber;
}

As you can see, the subscriber property is used to hold the user information retrieved from the database. Note that you don't need to define the set accessor for the subscriber property, because that property is supposed to be written to only within the doLogin method but never in the code that will use the Login class. That's why it is best to give only read access to the subscriber property. Another important thing to note about the doLogin method is that this method focuses entirely on the database and doesn't contain any UI-specific code.

Next, turning back to the Subscriber class, add the following two public methods to the class:

public void save() throws Exception {
try{
Utils.getDBUtils().insert(this);
}
catch (Exception e) {
throw new Exception(e.toString());
}
}
public void remove() throws Exception {

try{
Utils.getDBUtils().delete(this);
}
catch (Exception e) {
throw new Exception(e.toString());
}
}

As you can see, neither of the above methods contains any UI-specific code. You might be wondering why we needed the sample application to achieve such a clear separation between its layers. In fact, this separation will make it easier to demonstrate which layers of the application really need to be modified and which layers can stay intact when you begin XML-enabling the application.

Developing backing beans. This subsection focuses on the backing beans you will use in the sample application. The next subsection illustrates how to configure the application to use the backing beans discussed here.

The most important features of a backing bean are the properties and methods associated with the UI components used on the page. For the sample application, you don't need to create backing beans from scratch. Instead, you extend the model beans discussed earlier with UI-specific code. Move on now to the ViewController project and create the LoginBean backing bean extending the Login model bean as follows:

package jsfsubscr.view;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import jsfsubscr.model.Login;

public class LoginBean extends Login
{
public LoginBean()
{
}
public String login()
{
FacesContext ctx = FacesContext.getCurrentInstance();
ValueBinding bnd = ctx.getApplication().createValueBinding("#{subscr}");
bnd.setValue(ctx, null);
try {
doLogin();
}
catch (Exception e) {
Logger.getLogger("jsfsubscr.view").log(Level.SEVERE, e.getMessage(), e);
ctx.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(),""));
return "loginFailure";
}
return "loginSuccess";
}
}

As shown here, the LoginBean bean extends the Login bean with a single method, namely login. As you will learn later in the “Designing JSF Pages” subsection, the login method is an action method and is called when a user clicks on the Login button on the login.jsp page. The first thing the login method does is to set the subscr managed bean to null, because it may hold the information about the previous user. (As you will learn in the “Using the Managed Bean Facility” subsection, the subscr managed bean represents a SubscriberBean object that is used in the application to hold and manipulate the current user's information.) Next, the login method calls the doLogin method and returns a logical outcome string for the navigation handler. In general, the navigation handler is responsible for choosing the next JSF page to be displayed based on either the logical outcome of an action method in a backing bean or the hardcoded outcome that the action attribute of a UICommand component defines. In the example, the login action method generates a logical outcome string depending on whether the username and password are legitimate.

Next, in the ViewController project you create the SubscriberBean that extends the Subscriber model bean discussed previously:

package jsfsubscr.view;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import jsfsubscr.model.Subscriber;

public class SubscriberBean extends Subscriber
{
public SubscriberBean()
{
}
public String unsubscribe()
{
try {
remove();
}
catch (Exception e) {
Logger.getLogger("jsfsubscr.view").log(Level.SEVERE, e.getMessage(), e);
FacesContext ctx = FacesContext.getCurrentInstance();
ctx.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(),""));
return "unsubscrFailure";
}
return "unsubscrSuccess";
}
public String subscribe()
{
try {
save();
}
catch (Exception e) {
Logger.getLogger("jsfsubscr.view").log(Level.SEVERE, e.getMessage(), e);
FacesContext ctx = FacesContext.getCurrentInstance();
ctx.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(),""));
return "subscrFailure";
}
return "subscrSuccess";
}
}

The backing beans discussed here extend the model beans with UI-specific code. Of course you might do without model beans at all, encapsulating all the business logic—including database access calls—in backing beans. However, this would violate the separation of presentation and business logic, making the code less maintainable.

Using the managed bean facility. Thankfully, you don't need to write code responsible for creating backing bean instances—JSF provides an efficient mechanism, called the managed bean facility, that makes the beans available to the application when the UI components reference them. You configure the managed bean facility in the application configuration file, normally named faces-config.xml. This file is also used to define converters, validators, and navigation rules (among other JSF application elements) for the application to use. In Oracle JDeveloper 10.1.3, the faces-config.xml configuration file is automatically created in the ViewController project when you create a new application based on the Web Application [JSF, JSP, EJB] template. Moreover, JDeveloper provides you with the graphical interface for specifying managed beans and navigation rules in the faces-config.xml file.

Now add the managed bean declarations for SubscriberBean and LoginBean to the faces-config.xml file as follows:

<faces-config xmlns="http://java.sun.com/JSF/Configuration">
...
<managed-bean>
<managed-bean-name>subscr</managed-bean-name>
<managed-bean-class>jsfsubscr.view.SubscriberBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>

<managed-property>
<property-name>email</property-name>
<value>#{login.subscriber.email}</value>
</managed-property>
<managed-property>
<property-name>password</property-name>
<value>#{login.subscriber.password}</value>
</managed-property>
<managed-property>
<property-name>name</property-name>
<value>#{login.subscriber.name}</value>
</managed-property>
<managed-property>
<property-name>phone</property-name>
<value>#{login.subscriber.phone}</value>
</managed-property>

</managed-bean>
<managed-bean>
<managed-bean-name>login</managed-bean-name>
<managed-bean-class>jsfsubscr.view.LoginBean</managed-bean-class>
<managed-bean-scope> session</managed-bean-scope>
</managed-bean>
...
</faces-config>

Note that the subscr bean contains four managed-property elements. Each of them uses a value-binding expression inside its value element to associate the corresponding property of the subscr bean with the appropriate property of the Subscriber object, which itself is a property of another bean, namely login. In fact, this example illustrates a standard mechanism that is normally used in JSF applications to chain beans together.

Configuring navigation rules. As I mentioned previously, the navigation handler is responsible for selecting the next JSF page to be displayed based on either the logical outcome of an action method in a backing bean or the outcome that the action attribute of a UICommand component defines. In either case, however, the action outcome must match an outcome in a navigation rule defined in the application configuration file. The navigation rules for the sample application are shown in Listing 2 (sample code download).

Looking through the navigation rules shown in Listing 2 (Oracle JDeveloper 10.1.3 includes a JSF navigation model diagrammer, in fact), you may notice that the presentation layer of the sample application contains only three pages: login.jsp, welcome.jsp, and subscribe.jsp. The first page of the sample application is login.jsp. On this page, the user can either enter his credentials to log in or click on the subscription link to move on to the subscribe.jsp page. Upon successful login the user is taken to the welcome.jsp page, which displays a welcome message, the user's subscription information, the logout link, and the unsubscribe link that references the unsubscribe method in the SubscriberBean.

Designing JSF pages. Next, you create JSF pages for the application in the ViewController project. In Oracle JDeveloper 10.1.3, you can build a JSF enabled JSP page by using the Create JavaServer Faces (JSF) JSP wizard, which is invoked by selecting File->New->Web-Tier->JSF->JSF JSP. This wizard automatically creates the backing bean for the generated JSF page and inserts the corresponding managed bean element in the faces-config.xml file. But because you have already created backing beans and configured the faces-config.xml file for the sample application, you might want to invoke another JDeveloper's wizard: Create JavaServer Pages (JSP) by selecting File->New->Web-Tier->JSP->JSP. This wizard also allows you to create a JSF JSP page -- but unlike the former it doesn't automatically create the backing bean for that page.

Because the login.jsp page is the first page the user sees when the application starts up, you might want to start designing JSF pages for the sample application with this page. The source for the login.jsp is shown in Listing 3 (sample code download).

You can see in the listing that the login.jsp contains two forms. The first includes a commandLink tag whose action attribute is set to subscribe. The user clicks on this link to move on to the subscribe.jsp page. The second is a login form, which includes the tags that represent the fields for entering credentials as well as the commandButton tag that represents the Login button used to submit the form. Specifically, when the user clicks on the Login button on the login.jsp page, the login method of the LoginBean instance is invoked.

As mentioned, the user is taken to the subscribe.jsp page by clicking on the subscribe link on the login.jsp page. On the subscribe.jsp page, the user must fill out all the fields and then click on the subscribe button to save the information in the database. Listing 4 includes the subscribe.jsp file.

Looking through the listing, you may notice that the selectBooleanCheckbox tag contains the validator attributethat refers to the subscribedCheck method of the SubscriberBean backing bean. This method simply checks whether the user has checked the subscribed checkbox. If the checkbox is checked, it takes no further action. Otherwise, the subscribedCheck method generates a new FacesMessage, then associates the generated FacesMessage with the subscribed component, and finally sets a false flag that indicates the value of the component is not valid.

public void subscribedCheck(FacesContext context, UIComponent component, Object value) 
{
if (value.toString()=="false") {
FacesMessage message = new FacesMessage("You must check this box to subscribe");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
String componentId = component.getClientId(context);
context.addMessage(componentId, message);
((EditableValueHolder) component).setValid(false);
}
}

Also, make sure to place the following import statements after the existing import statements in SubscriberBean.java:

import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;

Now you can get back to developing JSF pages. As mentioned, upon successful login the user goes to the welcome.jsp page, which displays the user's subscription information and allows the user to cancel her or his subscription by clicking on the unsubscribe link. Listing 5 (sample code) provides the source for the welcome.jsp page.

Servlet configuration. Finally, before you can deploy the sample application inside your application server, you need to create a Web application deployment descriptor. The fact is that JSF applications, like all J2EE Web applications, are configured via elements contained in a web.xml deployment descriptor. For a JSF application, the deployment descriptor above all must specify the servlet that will process JSF requests as well as the servlet mapping for the processing servlet. Oracle JDeveloper 10.1.3 automatically generates this file when you create an application based on the Web Application [JSF, JSP, EJB] application template. In most cases, you won't need to change something in the web.xml file that JDeveloper generates. The web.xml deployment descriptor for the sample application might look like sample code Listing 6.

To test out the sample application from JDeveloper, you simply right-click the login.jsp page in the Application Navigator window and choose Run from the context menu. As a result, JDeveloper runs the application on its embedded OC4J server, loading the login.jsp page in your browser, as shown in Figure 1.

Figure 1
Figure 1. The first page of the application

On the login.jsp page, you click the subscription link to open the subscribe.jsp page in which you are supposed to enter your credentials and additional information about yourself like that shown in Figure 2.

Figure 2
Figure 2. Providing information for subscription

When you have submitted the information entered on the subscribe.jsp page by clicking the Subscribe button, you are taken back to the login.jsp page where you are supposed to enter your credentials and click the Login button to login. Upon successful login you will be taken to the welcome.jsp page, which displays a welcome message, and the information that you provided when you subscribed. The welcome.jsp page should look like that shown in Figure 3.

Figure 3
Figure 3. Viewing the user's subscription information

At this point, you have created a simple JSF application that interacts with Oracle, moving relational data in and out of the database. The next sections show how to XML-enable this application so that it not only can talk XML to Oracle XML DB but also itself can operate on XML data.

Moving an Existing JSF Application to XML

Now that you have a JSF application, you might want to add XML functionality to it. Probably the first question you might ask is, “What should I do to make my JSF application talk XML to Oracle XML DB?” To start with, you might simply rewrite the SQL queries used in the DBUtils data access object, so that they can be used to access and manipulate XML instead of relational data. You can do this easily because Oracle XML DB provides the XML extensions of SQL that allow you to operate on XML data with standard SQL commands such as SELECT, UPDATE, and INSERT.

Designing XML Database. Before you proceed with XML-enabling the sample application, you need to create all the necessary database objects to store information about subscribers in XML format. To start with, you might want to create a subscriberXML table of XMLType designed to store subscriber records in XML format. It is important to note that XMLType is the native datatype used for storing XML data in Oracle Database. One way to create an XMLType table is by registering an annotated XML schema with the database. It is assumed that you will use an XML schema annotated with information to automatically generate object types and object tables. To register a subscriber annotated XML schema, you might use the PL/SQL code shown in sample code Listing 7.

The above XML schema defines the structure of a subscriber XML document and is registered under the URL http://localhost:8080/public/subscriber.xsd. (Note that Tomcat users using the default port 8080 may face a port conflict here.) When you register this schema against the database, Oracle will automatically create the subscr_t object type and subscriberXML table of XMLType. Since the subscriberXML table is constrained to the above XML schema, only the subscriber XML documents that conform to that XML schema can be stored in that table. When a subscriber XML document is validated against the schema, Oracle XML DB will decompose the document contents and store them as an instance of subscr_t in the subscriberXML table. Note, however, that Oracle XML DB will not perform a full XML schema validation when you insert or update XML content stored in the subscriberXML table. Instead it will simply check whether the XML document conforms to the object-relational storage. For example, if you try to insert a subscriber XML document whose password node contains a value larger than 30 characters, Oracle XML DB will generate the following error message:

ERROR at line 1:
ORA-30951: Element or attribute at Xpath /SUBSCRIBER/PASSWORD exceeds maximum length

But if you try to insert a subscriber XML document whose password node contains a value consisting of a single character, Oracle XML DB will have no objection. This is despite the fact that defining a one-character password violates the constraints defined in the subscriber XML schema for the password node:

<xs:simpleType name="PasswordType">
<xs:restriction base="xs:string">
<xs:minLength value="2"/>
<xs:maxLength value="30"/>
</xs:restriction>
</xs:simpleType>

As you see here, a value of the password node cannot be fewer than 2 characters according to the above constraints.

To avoid inserting invalid data, you need to explicitly call the schemaValidate member procedure of XMLType. One way of doing this is to define a TRIGGER BEFORE INSERT on the XMLType table, as follows:

CREATE TRIGGER validSubscr BEFORE INSERT ON subscriberXML FOR EACH ROW
DECLARE
newdoc XMLType;
BEGIn
newdoc := :new.object_value;
xmltype.schemavalidate(newdoc);
END;
/

If you try now to insert a subscriber XML document whose password node contains a value consisting of a single character, Oracle XML DB will generate the following error message:

ERROR at line 1:
ORA-31154: invalid XML document
ORA-19202: Error occurred in XML processing
LSX-00221: "q" is too short (minimum length is 2)
ORA-06512: at "SYS.XMLTYPE", line 333
ORA-06512: at "SCOTT.VALIDSUBSCR", line 5
ORA-04088: error during execution of trigger 'SCOTT.VALIDSUBSCR'

As this code shows, storing the application data in an XML Schema-based XMLType table allows you to constrain the data according to the rules specified in the XML schema. Although JSF has extensive support for validation, using XML schema constraints can also be useful in some situations. The fact is that the XML Schema specification offers more-sophisticated validation capabilities than JSF. For example, you might need to create a validator that ensures that the user has entered reasonable values in the fields corresponding to the related components. In fact, solving this problem by means of JSF can be quite tricky, because the validation mechanism JSF uses is intended to validate a single component. In contrast, XML Schema definition language allows you not only to specify validation rules for single elements but also to define uniqueness and reference constraints on the contents of multiple elements.

Turning back to the subscriberXML table that Oracle had to automatically create when you registered the subscriber XML schema discussed above, make sure to define the PRIMARY KEY constraint on the EMAIL element to ensure that the value of the EMAIL element is NOT NULL and is unique across a set of XML documents stored in the subscriberXML table. You can do this with the following SQL command:

ALTER TABLE subscriberXML
ADD constraint EMAIL_PRIMARYKEY
PRIMARY KEY (xmldata."EMAIL");

Table altered.

Interacting with Oracle XML DB. Now that you have created the database schema to store subscriber XML documents, you can return to the JSF application discussed in the preceding section. As mentioned earlier, to make the application talk XML to Oracle XML DB, you simply need to rewrite the SQL queries used in the DBUtils data access object. You might start by changing the sql string in the select method of DBUtils as follows:

String sql = "SELECT EXTRACTVALUE(object_value, '//EMAIL')," +
"EXTRACTVALUE(object_value, '//PASSWORD')," +
"EXTRACTVALUE(object_value, '//NAME')," +
"EXTRACTVALUE(object_value, '//PHONE')," +
"EXTRACTVALUE(object_value, '//SUBSCRIBED')" +
"FROM subscriberXML WHERE EXTRACTVALUE(object_value, '//EMAIL')=" +
"'" + email + "'";

Next, move on to the insert method and change the SQL string as follows:

String sql = "INSERT INTO subscriberXML VALUES(XMLTYPE('<SUBSCRIBER>" +
"<EMAIL>" + email + "</EMAIL>" +
"<PASSWORD>" + password + "</PASSWORD>" +
"<NAME>" + name + "</NAME>" +
"<PHONE>" + phone + "</PHONE>" +
"<SUBSCRIBED>" + subscribed + "</SUBSCRIBED>" +
"</SUBSCRIBER>').createSchemaBasedXML('http://localhost:8080/public/subscriber.xsd'))";

Finally, change the SQL string in the delete method of DBUtils as follows:

String sql = "DELETE SubscriberXML WHERE EXTRACTVALUE(object_value, '//EMAIL')=" +
"'" + email + "'";

That's it. Note that you simply had to change the SQL queries, which the data access object uses, in order to make the sample application talk XML to Oracle XML DB. If you now test the sample application, you will see that it works as before and that the changes made do not affect the application behavior from the user's point of view. This is an example of how you can benefit from using the MVC architecture—you can enhance the application functionality with new features by making changes to only one application layer. In the above example, to add XML functionality to the application, you didn't even need to revise the model layer entirely. Instead, you simply made changes to the strings representing SQL queries that the DBUtils data access object uses.

Further XML Enabling

The main weakness of the application discussed in the preceding section is that it still operates on non-XML data. As discussed above, the application simply uses SQL/XML queries to move the necessary information in and out of the XML database. However, you can go further and make the application itself operate on XML data. Taking it one step further, the application not only can hold its data in XML but also can transform the XML structures used in the application into other XML documents as needed, using XSLT.

To implement the above-mentioned functionality, you will need to modify only the model layer of the sample application—the view layer, including backing beans, will stay intact. You start out with the Login model bean. Sample code Listing 8 shows the code in the Login.java after the required corrections have been made.

In regards to Listing 8, the main thing to note is that the Login model bean has been changed to use a DOM document to hold the login data. The trick is to rewrite the set and get methods of the properties designed to hold the login data so that these methods access and manipulate the data stored in a DOM document. For the first step, you create a DOM document in the constructor of the Login class. Then you add a single element to this DOM document. Next you rewrite all the accessor methods that are used to access and manipulate properties intended to hold the login data so that they operate on data stored in the DOM document. As you can see, in this simple example you don't even need to navigate the nodes of the DOM tree because it actually contains a single node, namely SUBSCRIBER. Instead, you manipulate the attributes of the SUBSCRIBER node. Finally, note that the Login.java class uses the Oracle XML Parser v2 library that is a part of Oracle XML Developer's Kit (XDK) 10g. If you use Oracle JDeveloper, there is nothing to worry about since it includes the Oracle XDK Java libraries by default. Moreover, you can always add newer releases of XDK libraries to JDeveloper. To do this, you use the Tools->Manage Libraries dialog box, where you can add new libraries as needed.

Once you have finished modifying the Login.java file, you should move on to the Subscriber.java file. Sample code Listing 9 shows the code for the Subscriber.java file that has been modified to hold the information about a subscriber in a DOM document.

Finally, move on to the DBUtils.java file, and modify it as shown in Listing 10. Examining the code presented there, you may note that only the insert and select methods have been modified and that others have no differences compared to the DBUtils class code shown in Listing 1. It is important to understand that both the insert and select methods are responsible for exchanging data between the application and the database.

The select method starts by defining the following SQL query string for querying the database:

SELECT (SYS_NC_ROWINFO$).getStringVal() FROM subscriberXML 
WHERE EXTRACTVALUE(object_value, '//EMAIL')=" + "'" + email + "'"

Executing the above query allows you to obtain a row as a string that might look like this:

<SUBSCRIBER>
<EMAIL>foo@mail.com</EMAIL>
<PASSWORD>pswd</PASSWORD>
<NAME>Joe</NAME>
<PHONE>(650)475-3457>/PHONE>
<SUBSCRIBED>1</SUBSCRIBED>
</SUBSCRIBER>

Next, you use the getBinaryStream method of the ResultSet object to convert the data retrieved from the database into an InputStream:

InputStream inStream=rsltSet.getBinaryStream(1);

Next you create an XSLProcessor object that is then used to apply the ElemToAttr.xsl XSLT stylesheet to the XML data source obtained from the InputStream:

XSLProcessor processor = new XSLProcessor();
URL xslInput = new URL("http://localhost:8080/public/ElemToAttr.xsl");
XSLStylesheet stylesheet = processor.newXSLStylesheet(xslInput);
XMLDocumentFragment result = processor.processXSL(stylesheet, inStream, null);

As a result, you obtain an XMLDocumentFragment that contains XML data like this:

<SUBSCRIBER EMAIL=" foo@mail.com" PASSWORD="pswd" 
NAME="Joe" PHONE="(650)475-3457" SUBSCRIBED="1"/>
As you can see, the ElemToAttr.xsl stylesheet simply transforms the SUBSCRIBER XML node obtained from the database into another SUBSCRIBER XML node that is then used in the application. The above example assumes that you have stored the ElemToAttr.xsl stylesheet file in the public folder of Oracle XML DB repository. One way to do this is by using FTP protocol. For the first step, you create the following ElemToAttr.xsl file:


<?xml version='1.0' encoding='utf-8' ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"><xsl:output method="xml"/>
<xsl:template match="/">
    <xsl:for-each select="/SUBSCRIBER" > 	
       <xsl:element name='{name()}'> 
           <xsl:for-each select="*" > 
                  <xsl:attribute name='{name()}'>
                       <xsl:value-of select="."/>
                  </xsl:attribute>
           </xsl:for-each> 
          </xsl:element>
    </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Then, you copy the ElemToAttr.xsl file to the user home directory. (In Windows, if no home directory is specified, the %SystemDrive% is assumed to be the home directory.) Finally, you upload the file to XML repository by using a standard command-line FTP tool as follows:

ftp> open localhost 2100
Connected to localhost.
220 localhost FTP Server (Oracle XML DB/Oracle Database 10g Enterprise Edition
Release 10.1.0.2.0) ready.
User (localhost:(none)): usr
331 pass required for USR
Password:
230 USR logged in

ftp> cd /public
250 CWD Command successful

ftp> put ElemToAttr.xsl
200 PORT Command successful
150 ASCII Data Connection
226 ASCII Transfer Complete
ftp: 512 bytes sent in 0.00 Seconds 512000.00Kbytes/sec

ftp> quit
221 QUIT Goodbye.

Returning to the select method, you employ the DOM 3.0 LS API functions to transform the XMLDocumentFragment generated by the XSLProcessor into a string:

DOMImplementation imp = subscriber.getSubscr_node().getOwnerDocument().getImplementation();
DOMImplementationLS impls=(DOMImplementationLS) imp;
LSSerializer domWriter = impls.createLSSerializer();
String subscrXML = domWriter.writeToString(result);

Finally, you obtain an XMLElement from the subscrXML String representing a SUBSCRIBER node transformed for the application to use:

DOMParser dp = new DOMParser();
dp.parse(new StringReader(subscrXML));
XMLDocument xd = dp.getDocument();
XMLElement elm = (XMLElement) xd.getDocumentElement();
subscriber.setSubscr_node((Element)elm);

Unlike the select method that obtains XML data from the database and then transforms that data into XML for the application to use, the insert method performs the opposite operation, producing XML to be stored in the database from the XML data that the application uses. To perform this transformation, the insert method uses the AttrToElem.xsl stylesheet, shown below:

<?xml version='1.0' encoding='utf-8' ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml"/>
<xsl:template match="/">
<xsl:for-each select="/SUBSCRIBER">
<xsl:element name="{name()}">
<xsl:for-each select="@*">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
To upload this file into the public folder in Oracle XML DB repository, you might use FTP protocol, as discussed previously.

When you have finished modifying the Login.java, Subscriber.java, and DBUtils.java files, you can test out the sample application. Despite the fact that it should work as before from the user's point of view, you have just created an XML-enabled JSF application that not only talks XML to Oracle XML DB but also itself operates on XML content using the capabilities of DOM 3.0 and XSLT.

Conclusion

In this article I have demonstrated a few ways to XML-enable an existing JSF application. If your JSF application uses an Oracle database as its repository for the application data, the simplest way to make your JSF application talk XML to the database is first to create the necessary database objects for storing the application data in XML format and then use SQL/XML functions to manipulate XML with SQL statements. A weakness of this approach is that the application itself still operates on non-XML data. Therefore, you might want to proceed with XML-enabling your JSF application, so that it not only can talk XML to Oracle XML DB but also itself can manipulate XML as needed.

Yuli Vasiliev is a software developer, freelance author, and consultant who focuses mainly on Oracle objects and Oracle XML technology.

Send us your comments

E-mail this page
Printer View Printer View
Oracle Is The Information Company About Oracle | Oracle RSS Feeds | Careers | Contact Us | Site Maps | Legal Notices | Terms of Use | Privacy