BEA Logo BEA WebLogic Server Release 4.5.1

  Corporate Info  |  News  |  Solutions  |  Products  |  Partners  |  Services  |  Events  |  Download  |  How To Buy

   Introduction to BEA WebLogic Server 4.5:   Previous topic   |   Next topic   |   Contents   |  Index

 

WebLogic Server development facilities

 

WebLogic Server provides developers with a comprehensive set of Java APIs (Application Programming Interfaces) and a powerful deployment environment.

BEA is an enthusiastic supporter and participant in Sun's Java Community Process, the open specification development process that has produced several standard Java technologies implemented in WebLogic Server. BEA has committed to ongoing full support for these Java standards, including Sun's J2EE (Java 2 Platform, Enterprise Edition) announced in June 1999. WebLogic Server offers the most complete and mature J2EE support available.

This chapter introduces the WebLogic Server development APIs. Our online documentation includes a detailed Developers Guide for each of the APIs described here. Javadoc references for WebLogic APIs are also available online. The Javadoc references for Sun APIs are available on the Sun web site.

This chapter contains the following sections:

About the examples in this chapter

This chapter contains several examples. If you have the WebLogic Server 4.5 CD, you can find the source code for the examples in the directory /documentation/docs/examples/intro. If you do not have the CD, get the source code from our web site at http://www.weblogic.com/docs45/examples/intro/index.html.

Make a copy of the examples in a directory where you can edit and compile them.

To access and run the examples, first install WebLogic Server, following instructions in Setting up and running WebLogic Server. The WebLogic Server distribution includes scripts to set up your environment for running WebLogic Server and building examples to use with WebLogic Server. See Setting up your development environment to learn how to edit and execute these scripts before you try the examples in this chapter.

The example code presented in this document assumes that you are using a Java Developers Kit that is compatible with the JavaSoft JDK. The Java compiler is named javac, the JVM is named java, and options are provided to these utilities as defined by the JavaSoft JDK. You can use another Java development environment if it is certified for use with WebLogic Server. (See our Platform support page for a list of certified JDKs.) For example, you can use the Microsoft Developers Kit for Java with WebLogic. The Java tools in the Microsoft Developers Kit are different than those provided in the JDK. The Java compiler, for example, is named jvc.exe and the JVM is named jview.exe. Command options are also supplied differently. If you use a non-JavaSoft JDK, you will have to translate the instructions in this chapter to work with your tools.

The Enterprise JavaBean example requires the jar tool to package the bean for deployment on WebLogic Server. The Microsoft Developers Kit does not provide a jar command. To try this example, and the JMS example that uses the bean, you must install the JavaSoft JDK.

Servlets

A servlet is a server-side Java class that can be invoked and executed, usually on behalf of a client. Don't confuse servlets with applets; a servlet does its work on the server, but an applet does its work on the client. Several WebLogic Server services are implemented using servlets.

A servlet has input and output objects that represent a request and response, respectively. When a client requests a servlet, WebLogic Server loads the servlet, calls its init() method, and passes the request and response objects to a service method.

WebLogic Server supports the javax.servlet.GenericServlet and javax.servlet.http.HttpServlet interfaces. An HttpServlet extends the GenericServlet interface, adding support for the HTTP protocol.

The HTTP protocol concerns client requests and server responses. Clients are usually web browsers and servers are usually web servers, including servlet engines like WebLogic Server. Other web servers can be configured to redirect their servlet requests to WebLogic Server.

HTTP servlets (and JSP, which is based on HTTP servlets) are the components that represent the server-side presentation logic in e-commerce applications. This section concentrates on HTTP servlets.

HTTP servlets extend the javax.servlet.http.HttpServlet class, which allows them to run in any compliant servlet engine. The work performed by HTTP servlets can be compared to that of CGI scripts, but HTTP servlets are easier to write, faster than running an external script, and they have access to other WebLogic Server services.

The javax.servlet.http package provides the Java interfaces that allow your code to participate in the HTTP protocol. An HTTP servlet is called with an HttpServletRequest object, which contains data received by the server from the client, and an HttpServletResponse object, where the servlet can write data to return to the client.

A servlet can extract standard headers from the client request. When clients enter data in HTML forms, the data is transferred to the servlet as a set of parameters in the HTTP stream. The servlet can read the parameters by calling the HttpServletRequest object's getParameterValues() method.

To respond to an HTTP request, a servlet uses the HttpServletResponse object. The methods on this object let the servlet take such actions as setting headers in the HTTP response, adding a cookie to a response, or returning an error. The HttpServletResponse also provides a PrintWriter to the servlet, which the servlet uses to write HTML. Since the HTML is generated for each request to the servlet, it can be highly dynamic.

Many web applications require a coordinated series of requests and responses so they can get input from the client and return the correct response. To link HTTP requests and retain data over a series of calls, many web applications make use of URL parameters and browser cookies. With URL parameters, data is appended to the URL a client uses to make a request. Cookies allow a server to store an object in the client's web browser for later access, if the browser user permits it.

The javax.servlet.http.HttpSession interface provides a convenient method for managing a series of HTTP events in a servlet. This interface allows a servlet to create a session object in the servlet and store data in it. The session remains available to the servlet when the same client returns. WebLogic Server ties the session data it maintains to a particular browser session by setting a cookie in the browser. If the user has disallowed cookies, the servlet can still use sessions by coding the session ID in the URL. See Using WebLogic HTTP Servlets for information on URL rewriting.

WebLogic Server can be configured as a fully functional web server that delivers any arbitrary MIME type to HTTP clients. For more information on configuring WebLogic Server HTTP services, see Setting up WebLogic as an HTTP server.

Creating an HTTP Servlet

A servlet class handles HTTP requests by overriding methods in javax.servlet.http.HttpServlet. A client can make an HTTP request to a servlet, specifying one of these service methods: GET, POST, PUT, DELETE, OPTIONS, or TRACE. The javax.servlet.http.HttpServlet class provides a default empty implementation for each of these methods. To create a servlet, you just override the methods you want to implement.

For example, when a user enters the servlet's URL into a browser, the browser sends a GET request to the servlet. The servlet engine sends the request to the servlet's service() method, which routes the GET request to the doGet() method. The default doGet() implementation returns an error 400, BAD_REQUEST, which is the correct response for a servlet that does not handle a GET request.

See the Javadocs for javax.servlet.HttpServlet on the Sun web site for a description of each of the service methods and their default implementations.

In HTTP servlets, it is common to override the doGet() and doPost() methods, less common to override the other methods. With generic servlets, all requests are passed to the service() method.

A servlet can override the init() method, which is called whenever the servlet is loaded or reloaded. It can perform any initialization work that needs to be done before it responds to client requests, but it must first call the init() method in the parent class. The init() method is commonly used to retrieve configuration parameters for the servlet which, in WebLogic Server, are set in the weblogic.properties file. You can override the destroy() method to perform any clean up required when the servlet exits.

The HTTP service methods, such as service(), doGet() and doPost(), are each called with an HttpServletRequest and an HttpServletResponse. The servlet gets information about the client's request from the HttpServletRequest and writes the response into the HttpServletResponse.

SqlServlet example

The SqlServlet.java example is a servlet that gets an SQL statement from a browser form, executes the statement using a connection from a WebLogic Server connection pool, and constructs an HTML page containing the query results. Here is an example of the output from this servlet:

The SqlServlet example demonstrates the following:

init() method

The init() method of a servlet does whatever initialization work is required when WebLogic Server loads the servlet. The default init() method does all of the initial work that WebLogic Server requires, so you don't need to override it unless you have special initialization requirements. If you do override init(), first call super.init() so that the default initialization actions are done first.

The SqlServlet init() method gets the name of the JDBC connection pool it is to use. Setting the connection pool name as an argument makes it easy to redeploy the servlet with a different database.

Here is the beginning of the SqlServlet.java example, including the init() method:

Listing 2-1 SqlServlet.java


package examples.intro;
/*
* @author Copyright (c) 1999. BEA Systems, Inc.
* All rights reserved.
*/

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
import weblogic.common.*;


/**
* This example servlet takes an SQL query from a form and
* executes it using a connection borrowed from a connection
* pool in WebLogic Server.
*
* The name of the connection pool is specified in an initArg
* for the servlet in the <tt>weblogic.properties</tt> file.
*/

public class SqlServlet extends HttpServlet
{
String compile;

/**
* The init method performs any tasks that need to be
* done when a servlet first starts up. For this servlet,
* there is one task, which is to look up the name of the
* connection pool to use for SQL queries. The
* connection pool is set in the servlet's initArgs
* property in the <tt>weblogic.properties</tt> file.
*/
public void init(ServletConfig config)
throws ServletException
{
super.init(config);
connPool = getInitParameter("connectionPool");
if (connPool == null) {
System.err.println("This servlet requires a " +
" connectionPool initArg." );
throw new UnavailableException (this,
"No JDBC connection pool was specified.");
}
}


SqlServlet imports javax.servlet.* and javax.servlet.http.* and extends HttpServlet. The java.sql and weblogic.common packages are imported because this servlet uses JDBC and WebLogic JDBC connection pools. The java.io package is imported for the I/O methods of the PrintWriter, which is a stream object.

Our purpose in overriding the init() method is to retrieve the name of the connection pool. In the weblogic.properties file, this servlet is registered with properties similar to these:

weblogic.http.register.SqlServlet=\
examples.intro.SqlServlet

weblogic.http.initArgs.SqlServlet=\
connectionPool=demoPool

The first property registers the servlet with the name SqlServlet. The second passes a single argument, connectionPool, which is set to the name of a JDBC connection pool that is also configured in the WebLogic Server.

The init() method retrieves the argument by calling getInitParameter("connectionPool").

doGet() method

The URL for a servlet in WebLogic Server is the URL of the WebLogic Server followed by the registered name of the servlet. The URL for the SqlServlet servlet is:

http://host:port/SqlServlet

where host is the name of the computer running WebLogic Server and port is the port number where WebLogic Server is listening for connections. If a user requests this URL in a browser, WebLogic Server passes the request to the doGet() method of the SqlServlet servlet.

doGet(), and the putForm() method it calls, construct an HTML form to get an SQL query from the client:

Listing 2-2 SqlServlet.java doGet() method


  /**
* The doGet() method is called when a Servlet is first
* requested. This method prints a query form to get
* the query from the browser. Once a client has this
* form, all of their subsequent calls to this servlet
* should be through the doPost() method.
*/
public void doGet(HttpServletRequest req,
HttpServletResponse res)
throws ServletException, IOException
{
res.setContentType("text/html");
PrintWriter pw = res.getWriter();
pw.println("<html><head><title>SQL Query Servlet");
pw.println("</title></head>");
pw.println("<body bgcolor=#ffffff><font face=\"Helvetica\">");
pw.println("<h1><font color=\"#DB1260\">");
pw.println("SQL Query Servlet</font></h1>");
pw.println("<hr><p>");
putForm(pw, req, "");
pw.println("</body></html>");
pw.close();
}

  /**
* putForm() writes an HTML form to the response to collect
* the sqlStatement parameter.
*/
public void putForm(HttpServletRequest req,
PrintWriter pw, String query)
throws IOException
{
pw.println("<form action=\"" +
req.getRequestURI() +
"\" method=post>");
pw.println("Enter an SQL query:");
pw.println("<center><p>");
pw.println("<textarea name=\"sqlStatement\" " +
"rows=\"8\" cols=\"64\" wrap=hard>" +
query + "</textarea>");
pw.println("<p><input type=\"submit\" " +
"name=\"sendSql\" value=\"Send SQL\">");
pw.println("</center>");
pw.println("</form>");
}


doGet() sets the MIME type of the page it is generating to "text/html", and then gets a PrintWriter from the HttpServletResponse. Then it writes out standard HTML through the PrintWriter. The HTML code for the form is in a separate method so that it can be reused by the doPost() method.

doGet() and putForm() have no dynamic HTML content. They could be implemented as static HTML pages. The <form> tag addresses the contents of the form to the SqlServlet servlet. The action attribute gives the URL of the servlet, and the method attribute specifies the HTTP POST method. The URL in the example code is relative to the servlet. If you want to send the data to another WebLogic Server or to another servlet, you can give the complete URL in the action attribute.

doPost() method

doPost() is called when a client clicks the "Send SQL" button on the form. The name attributes in the form element tags become the parameter names used to transport the form input in the HTTP request. In a doPost() method, parameters are retrieved by calling getParameterValues() on the HttpServletRequest. getParameterValues() returns an array of Strings, or null. The SqlServlet servlet is only interested in the first element of the array returned by the sqlStatement parameter, which contains the SQL statement the user entered:

Listing 2-3 SqlServlet.java doPost() method


  /**
* This method handles a POST request from a client. It
* gets the SqlStatement parameter from the HttpServletRequest
* object, executes the SQL on a connection from the connection
* pool, and generates an HTML page with the query results.
*/
public void doPost(HttpServletRequest req,
HttpServletResponse res)
throws ServletException, IOException
{
// first, set the "content type" header of the response
res.setContentType("text/html");

//Get the response's PrintWriter to return text to the client.
PrintWriter pw = res.getWriter();
pw.println("<html><head><title>" +
"SQL Query Results</title></head>");
pw.println("<body bgcolor=#ffffff><font face=\"Helvetica\">");
pw.println("<h1><font color=\"#DB1260\">");
pw.println("SQL Query Results</font></h1>");
pw.println("<hr><p>");

try {
String theQuery = req.getParameterValues("sqlStatement")[0];
pw.println("<p><i>Query:</i> " + theQuery + "<p>");

doQuery(req, theQuery, pw); // all the JDBC is in this method
pw.println("<p><hr>");
putForm(pw); // ask for another SQL statement
pw.println("<p><hr></body></html>");
}
catch (Exception e) {
pw.println("<p><hr>A problem occurred:");
pw.println("<pre>" + e.getMessage() + "</pre>");
}
finally {
pw.close();
}
}


doPost() writes the HTML for the response, but it calls doQuery() to handle all of the JDBC and write out the HTML-formatted query results, and putForm() to add the query form following the results.

Support methods

doPost() calls doQuery() to handle all of the JDBC processing for the query. The query could be any text-doQuery() just sends it on to the JDBC driver. The result of the query could be a syntax error or some other exception, a row update count, or a set of rows. Some SQL commands, such as stored procedures, could return multiple result sets. The try block in doQuery() shows how to process an arbitrary ResultSet.

Listing 2-4 SqlServlet.java doQuery() method


  /**
* doQuery() executes the SQL query.
*/
public void doQuery(String theQuery, PrintWriter pw)
throws Exception
{
// set up the JDBC connection
Connection conn = null;
Class.forName("weblogic.jdbc.pool.Driver").newInstance();
conn = DriverManager.getConnection("jdbc:weblogic:pool:"
+ connPool);
Statement s = conn.createStatement();
ResultSet rs = null;

try {
s.execute(theQuery);
// loop through the result sets
while (true) {
rs = s.getResultSet();
int rowcount = s.getUpdateCount();
if (rs == null && rowcount == -1)
break;
if (rs != null)
// call another method to construct a table
// from the ResultSet
printResults(pw, rs);
else
pw.println("Update count = " + rowcount);
s.getMoreResults();
}
}

catch (SQLException se) {
while (se != null) {
pw.print("<p><hr>SQL Exception: ");
pw.println("<pre>" + se.getMessage() + "</pre>");
se = se.getNextException();

}
}
finally {
try { s.close(); } catch (Exception e) {};
try { conn.close(); } catch (Exception e) {};
}
}


The printResults() method outputs a ResultSet in a formatted HTML table. It gets the metadata from the ResultSet, which provides access to the column names. It loops through the result rows, adding an HTML row for each.

Listing 2-5 SqlServlet.java printResults() method


  /** This method is called from doPost to format the
* query results as an HTML table.
*/
static void printResults(PrintWriter pw, ResultSet rs)
throws IOException, SQLException
{
ResultSetMetaData md = rs.getMetaData();
int columns = md.getColumnCount();
pw.println("<table border=1 bgcolor=\"#FFFFCC\" " +
"width=\"100%\">");
pw.println("<tr>");
for (int i = 0 ; i < columns; i++ ) {
pw.println(" <th align=left>" +
md.getColumnLabel(i+1) + "</th>");
}
pw.println("</tr>");
while (rs.next()) {
pw.println("<tr>");
for (int i = 0; i < columns; i++) {
Object val = rs.getObject(i+1);
if (val == null)
pw.println(" <td><br></td>");
else
pw.println(" <td>" + val.toString().trim()
+ "</td>");
}
pw.println("</tr>");
}
pw.println("</table>");
rs.close();
}


Running the SqlServlet example

  1. Set your development environment as described in Setting your development environment.

  2. In your weblogic.properties file, if you have not set up a JDBC connection pool, uncomment the weblogic.jdbc.connectionPool.demoPool property to use the Cloudscape evaluation database.

  3. Make sure there is an ACL (Access Control List) for the connection pool that includes either "guest" or "everyone". If you are using the Cloudscape demoPool connection pool, for example, uncomment the property:

    weblogic.allow.reserve.weblogic.jdbc.connectionPool.demoPool=\
    everyone

    This property is needed because SqlServlet does not specify a username and password when it gets the connection from the pool. Whenever an unauthenticated client attempts to use a WebLogic service, WebLogic Server assigns the client the "guest" user identity. If the connection pool does not have an ACL that allows "guest" to use the pool, the servlet will get an exception with a message such as: Pool connect failed: User "guest" does not have Permission "reserve" based on ACL "weblogic.jdbc.connectionPool.demoPool".

  4. Register the servlet by adding these properties:

    weblogic.http.register.SqlServlet=\
    examples.intro.SqlServlet

    weblogic.http.initArgs.SqlServlet=\
    connectionPool=demoPool

    If you are not using the Cloudscape connection pool, change demoPool to the registered name of your connection pool.

  5. In your development shell, change to the directory where you copied the examples, and compile SqlServlet.java into the SERVLET_CLASSES directory:

    Windows NT:

    javac -d %SERVLET_CLASSES% SqlServlet.java

    UNIX:

    javac -d $SERVLET_CLASSES SqlServlet.java

  6. Start WebLogic Server.

  7. In a web browser, enter the URL for the servlet. For example, if WebLogic Server and your browser are on the same computer, enter http://localhost:7001/SqlServlet. Otherwise, change localhost to the name of the computer running WebLogic Server.

  8. Enter a SQL query in the browser form and click the "Send SQL" button.

    You can enter any SQL DDL or DML statement that is valid for the target database. Since SqlServlet uses a JDBC Statement to execute the SQL, it can only handle results that can come from a Statement. For example, it does not handle procedure parameters.

More about Servlets

Learn more about writing HTTP Servlets in the developers guide, Using WebLogic HTTP Servlets.

You can find javadocs for the javax.servlet.http.HttpServlet package on the Sun website .

To configure WebLogic Server as an HTTP server, see the administration document Setting up WebLogic as an HTTP server.

JSP (JavaServer Pages)

JSP (JavaServer Pages) is a standard for combining Java and HTML to provide dynamic content in web pages. JSP is part of Sun's Java 2 Platform, Enterprise Edition. WebLogic Server 4.5 implements the JSP 1.0 specification.

JSP depends upon HTTP servlets-the JSP compiler generates the source code for an HTTP servlet from a .jsp file. The Java classes produced using the HTTP servlet and JSP are essentially equivalent. You can use either technology to create dynamic web pages.

JSP emphasizes page design with standard HTML formatting, so it is more convenient to use JSP for creating dynamic web pages than it is to write a Java HTTP servlet.

With JSP, you embed Java code in HTML using special JSP tags similar to HTML tags. You install the JSP page, which has a .jsp extension, into the WebLogic Server document root, just as you would a static HTML page. When WebLogic Server serves a JSP page to a client, it tests whether the file has been compiled. If not, it calls the WebLogic JSP compiler to compile the file into a servlet. A .jsp file is only recompiled when it has changed.

Although you can embed as much Java code as you like into a JSP page, using JavaBeans and Enterprise JavaBeans lets you concentrate on page design and presentation in the JSP page and encourages you to move application logic into reusable components. Using beans from JSP pages also makes it possible for web page designers to create and maintain dynamic web pages without having to learn Java programming.

JSP tags

JSP defines the following tags to enclose Java code fragments and directives for the JSP compiler:

<%! declaration %>
Contains class-level declarations of Java variables or methods. The variables and methods declared can be referenced within other JSP tags on the page.

<% scriptlet %>
Contains a Java code fragment that executes when the JSP page executes.

<%@ include file="relativeURL" %>
Includes the contents of a file at compile time. The JSP compiler includes the file and processes its contents.

<%@ page attributes %>
Defines page-level attributes. Attributes are specified as name="value" pairs. Multiple attributes are separated by white space within the tag.

<%= expression %>
Writes the value of a Java expression into the response page.

<!-- <%= expression %> -->
Evaluates a Java expression within an HTML comment. The resulting comment is not rendered by the browser, but can be viewed in the page source.

<%-- comment --%>
Comments the JSP source. Comments written within these tags are not included in the HTML output.

<jsp:useBean ... />
Specifies a JavaBean to be used on the JSP page.

<jsp:setProperty ... />
Sets JavaBean properties.

<jsp:getProperty ... />
Gets JavaBean properties.

JSP tags can also be written with XML tags. See Using WebLogic JSP for a table of equivalent tags.

Implicit variables

JSP provides a number of implicit variables that can be referenced on any JSP page. The JSP compiler adds these class variable declarations to the generated servlet code:

HttpServletRequest request;   // Request object
HttpServletResponse response; // Response object
HttpSession session; // Session object for the client
ServletConfig config; // ServletConfig for this JSP
ServletContext application; // Servlet context
PageContext pageContext; // Page context for this JSP
Object page; // Instance of the implementation
// class for this JSP (i.e., 'this')
JspWriter out; // PrintWriter to the browser

The request and response variables provide access to the HTTP request and response objects. A JSP page can extract information from the request and write information on the response using methods from the javax.servlet.http package.

The session variable allows WebLogic Server to save state for a client between requests on a JSP page. You must set the "session=true" attribute in the JSP page directive so that the JSP compiler includes support for sessions. Then you can save arbitrary values and retrieve them again on subsequent requests with the getValue() and putValue() methods on the session.

The config variable provides access to the javax.servlet.ServletConfig object for the JSP page. The methods on this object return initialization parameters for the page. With WebLogic Server, you define initialization parameters by setting an initArg property when you register a servlet in the weblogic.properties file. To use initialization arguments with JSP pages, you must register the JSP page as a servlet.

The application variable gives you access to information about the environment of the JSP page and provides methods for writing messages in the WebLogic Server log.

The pageContext variable provides access to a javax.servlet.jsp.PageContext object. You can use methods on this object to access various scoped namespaces, servlet-related objects, and common servlet-related functionality.

The page variable references the current instance of the JSP page. page is declared as a java.lang.Object, so you must cast it to the name of the generated servlet class name if you want to use it. An easier alternative is to just refer to the current instance of the page with the Java this keyword.

The out variable is a Java PrintWriter you can use to write HTML to the browser page from inside JSP tags.

A simple JSP example

The Calendar.jsp example is a simple JSP page that calls a JavaBean to display a calendar for the current month. The source for the JavaBean is in the file CalendarBean.java.

CalendarBean gets the current date and provides two get methods, getTodayString() and getHtmlMonth(). getTodayString() returns the current date in a String such as "Thursday, October 14, 1999". getHtmlMonth() returns a String containing the calendar for the current month, formatted as an HTML table.

CalendarBean has one set method, setColor(), which is used to set the background color of the calendar.

Calendar.jsp shows how to specify a JavaBean, set its properties, and get its values from within a JSP page. The source of the JSP page is fairly simple, but it demonstrates how complicated web page elements can be delegated to reusable JavaBean components.

Here is the source for Calendar.jsp:

Listing 2-6 Calendar.jsp


<!doctype html public "-//w3c/dtd HTML 4.0//en">
<html>
<head><title>Calendar</title></head>

<%@ page
info="Calendar JSP example"
contentType="text/html"
%>
<jsp:useBean id="calendar"
scope="page"
class="examples.intro.CalendarBean"
/>
<jsp:setProperty name="calendar"
property="Color" value="#FFFFCC"
/>

<h1>Today is <jsp:getProperty name="calendar"
property="TodayString" /></h1>
<p>
<center>
<jsp:getProperty name="calendar" property="HtmlMonth" />
</center>

<p>
<hr>
<font face="Helvetica">
<p>This page executed by
<%= application.getServerInfo() %>.<br>
Copyright 1999 (C) BEA Systems, Inc.
All Rights Reserved.
</body>
</html>


The <jsp:useBean ... /> tag identifies the CalendarBean class and assigns it an ID (calendar), which is used to reference it in other JSP tags on the page. The scope attribute specifies that the instance of this bean is stored in the current page. Setting the scope to "session" would tell WebLogic Server to store the bean instance in the session so that any changes persist over multiple requests on the JSP page.

The <jsp:setProperty ... /> tag sets the background color of the HTML table. The JSP compiler translates this to a call to the CalendarBean.setColor() method.

The <jsp:getProperty ... /> tags are translated into calls to CalendarBean.getTodayString() and CalendarBean.getHtmlCalendar(). The results of those calls are embedded in the HTML output.

Here is an example of the output from the Calendar.jsp JSP page:

Running the Calendar.jsp example

To use Calendar.jsp, your WebLogic Server must be configured to serve JSP pages. Then you compile CalendarBean.java, copy Calendar.jsp into WebLogic Server's document root, and call the page in a browser. Here are the steps:

  1. If you haven't yet configured WebLogic Server for JSP, edit the weblogic.properties file in the WebLogic installation directory. Find and uncomment the weblogic.httpd.register.*.jsp and weblogic.httpd.initArgs.*.jsp properties. Edit the compileCommand attribute to point to the location of your Java compiler. Edit the workingDir attribute so that it points to an existing directory where the JSP compiler can store its generated files.

    For more help on these properties see Setting up WebLogic for JSP in "Using WebLogic JSP".

  2. Set up your development environment as described in Setting your development environment.

  3. Change to the directory where you copied the example files and compile the CalendarBean.java file.

    Windows NT:

    javac -d %SERVER_CLASSES% CalendarBean.java

    UNIX:

    javac -d $SERVER_CLASSES CalendarBean.java

  4. Copy the Calendar.jsp file into the document root of your WebLogic Server. By default, this is the myserver/public_html directory in your WebLogic installation.

  5. Start WebLogic Server. See Setting up and starting WebLogic Server for help.

  6. In a browser, enter the URL for the JSP page. For example:

    http://localhost:7001/Calendar.jsp

More about JSP

You can find more about using WebLogic JSP in the developers guide Using WebLogic JSP.

See the Sun JSP page to find JSP specifications, tutorials, and examples.

JDBC (Java Database Connectivity)

JDBC provides standard interfaces for accessing relational database systems from Java. To use JDBC, you need a JDBC driver for the database system you want to access. BEA provides JDBC drivers for Oracle, Microsoft SQL Server, and Informix Dynamic Server. JDBC drivers for Cloudscape and Sybase SQL Server are bundled with WebLogic Server. You can use any standard JDBC driver with WebLogic Server.

JDBC can be employed in two-tier and multitier configurations. In a two-tier configuration, a Java client loads the JDBC driver into its own JVM. If the JDBC driver is a type 2 driver-one that employs vendor-specific native client libraries-the client must have the database vendor's client libraries installed.

In a multitier configuration, WebLogic Server loads the JDBC driver into its JVM and establishes the connection to the database. Instead of loading the database-specific JDBC driver, the client loads the WebLogic JDBC driver, which is a pure-Java, database-independent driver. A multitier configuration is composed of two two-tier connections: the WebLogic JDBC pool driver between the client and WebLogic Server, and the vendor-specific JDBC driver between WebLogic Server and the database.

The advantages to using multitier JDBC are found in the additional services that WebLogic Server provides.

First, some JDBC drivers require vendor-supplied native libraries. Our jdbcKona/Oracle driver for Oracle databases is one of these. Using the multitier configuration, the native libraries only have to be installed on the computer running WebLogic Server. Clients can use the pure-Java WebLogic JDBC driver instead.

Second, WebLogic Server allows you to create a JDBC connection pool. WebLogic Server creates a set of database connections in the pool. Clients then get a connection from the pool and return it when they are finished. Since the connections are pre-established, the client does not wait for the database connection to be made, a process that can be very time-consuming, especially for e-commerce applications where many clients execute short-lived queries against the database.

WebLogic Server connection pools have several configurable administrative features. By configuring the maximum number of connections permitted in the pool, you can control the load on the database server. When the demand for connections is heavy, WebLogic Server can increase the number of connections in the pool. When the demand decreases, the additional connections can be dropped, shrinking the pool back to its minimum configuration. Connections can be automatically tested and refreshed to ensure that a client does not get a connection that has gone bad. If the database server goes down, connections are remade automatically when it recovers.

Multitier WebLogic JDBC applications

WebLogic JDBC provides multitier JDBC services to its clients. The client gets a JDBC connection from WebLogic Server, and WebLogic Server gets a JDBC connection to the back-end database. Before using WebLogic JDBC, all that is required is that a JDBC driver for the target database is installed on the computer running WebLogic Server and that its Java classes are in WebLogic Server's classpath.

Testing a multitier JDBC connection

BEA provides a simple way to test a multitier database connection. Start up WebLogic Server and use the WebLogic t3dbping utility to form a multitier connection to a database. If you have a running database server and its JDBC driver installed on the WebLogic Server computer, you can try this with your own database. The example here uses the Cloudscape database. (An evaluation version of the Cloudscape database is included with WebLogic Server so you can try out the WebLogic Server database-dependent examples.)

The following example assumes that you installed WebLogic Server and have not altered the configuration for Cloudscape. If you have problems with Cloudscape, see Using the Cloudscape database with WebLogic.

Here are the steps to test a multitier database connection to Cloudscape with t3dbping:

  1. Follow the instructions in Setting your development environment to set up a development shell. This ensures that you will have access to the t3dbping class.

  2. Start WebLogic Server. See Setting up and running WebLogic Server for help.

  3. In your development shell, enter the following command (on one line):

    java utils.t3dbping t3://localhost:7001 "" "" ""
    COM.cloudscape.core.JDBCDriver
    jdbc:cloudscape:demo

    If you are using your own database, type java utils.t3dbping to see examples for several database vendors.

t3dbping should display the following results:

Listing 2-7 t3dbping output


Success!!!

You can connect to the database in your WebLogic JDBC program using:

import java.sql.*;
import java.util.Properties;
import weblogic.common.*;

T3Client t3 = null;
Connection conn = null;

try {
t3 = new T3Client("t3://localhost:7001");
t3.connect();

Properties dbprops = new Properties();
dbprops.put("user", "");
dbprops.put("password", "");

dbprops.put("server", "");

Properties t3props = new Properties();
t3props.put("weblogic.t3.dbprops", dbprops);
t3props.put("weblogic.t3", t3);
t3props.put("weblogic.t3.driverClassName",
"COM.cloudscape.core.JDBCDriver");
t3props.put("weblogic.t3.driverURL",
"jdbc:cloudscape:demo");

Class.forName("weblogic.jdbc.t3.Driver").newInstance();
conn = DriverManager.getConnection("jdbc:weblogic:t3", t3props);

// Do something with the database connection
// and then close and disconnect in try blocks

} finally {
try {conn.close();} catch (Exception e) {;}
try {t3.disconnect();} catch (Exception e) {;}
}


The output from the t3dbping utility includes code that you can use to develop a multitier JDBC Java program. (Note that this example code does not use a connection pool and therefore does not include all of the benefits to using connection pools. The use of connection pools is discussed in the next section.) If you do not see these results, t3dbping displays a message that should help you diagnose the problem. Another useful debugging utility, dbping, lets you test the two-tier JDBC connection on the WebLogic Server computer to diagnose problems in the back-end connection. See Testing connections for help with dbping.

The t3dbping command has several arguments:

t3:localhost:7001
This argument tells t3dbping how to connect to WebLogic Server. It is a URL for a WebLogic Server. t3 is the protocol to use-the WebLogic T3 protocol. localhost is the name of the computer running WebLogic Server. "localhost" is the standard DNS alias for the current computer. If WebLogic Server is running on a different machine on the network, substitute the name of that computer. 7001 is the port where WebLogic Server is listening for standard (unsecure) connections. "7001" is the default port, which can be reconfigured in the weblogic.properties file.

"" ""
The first two "" values are for the database username and password. Cloudscape does not require them, so we marked their place on the t3dbping command line with empty strings. Most databases do require a username and password.

""
The third "" value is for the name of the database server or instance, depending on the database vendor's terminology. With Cloudscape, the database instance is specified with a Java system property, java.system.property.cloudscape.system.home, which is defined in the weblogic.properties file. So we again mark the position of the argument on the command line with an empty string.

COM.cloudscape.core.JDBCDriver
This is the Java class name of the JDBC driver you want WebLogic Server to load. To find the right class name for your database, you have to consult the JDBC driver documentation.

jdbc:cloudscape:demo
This is a database URL, which the JDBC driver uses to locate the database. The format of the database URL varies for each JDBC driver. Look in the JDBC driver documentation to construct a URL that is correct for your database. With Cloudscape, the third segment of the URL is the database name, in this case "demo."

Using a WebLogic JDBC connection pool

A WebLogic JDBC connection pool opens database connections when WebLogic Server starts up, if the pool is configured in the weblogic.properties file, or when the connection pool is created. The connections remain open as long as the connection pool is not destroyed. A client can "reserve" a connection from the pool, which gives the client sole access to the connection until it is returned to the pool. Creating a database connection is an expensive operation on most database systems, and using a connection pool can eliminate most of that expense.

There are three ways to create a connection pool:

Pools created with the WebLogic Console or with Java applications are not automatically recreated when WebLogic Server restarts. Dynamic connection pools can be useful for some applications that need a connection pool only while they are active. An application that creates a dynamic connection pool can also be set up as a WebLogic Server startup class, which has the same effect as defining the connection pool in the weblogic.properties file.

Defining a connection pool

A sample Cloudscape connection pool is defined (but commented out) in the weblogic.properties file included in the WebLogic Server distribution. Here are the properties that define the pool:

weblogic.jdbc.connectionPool.demoPool=\
url=jdbc:cloudscape:demo,\
driver=COM.cloudscape.core.JDBCDriver,\
initialCapacity=1,\
maxCapacity=2,\
capacityIncrement=1,\
props=user=none;password=none;server=none
#
# Add an ACL for the connection pool:
weblogic.allow.reserve.weblogic.jdbc.connectionPool.demoPool=\
everyone

The first property defines the connection pool, which is named "demoPool." The second property specifies which WebLogic users are allowed to reserve a connection from the pool. In this case, specifying the special user "everyone" means that any user can reserve a connection.

The url and driver arguments are familiar from the t3dbping example. The url, in the format specified by the JDBC driver vendor, allows the JDBC driver to locate the database. The driver is the full class name of the JDBC driver, which can be found in the documentation for the JDBC driver.

The initialCapacity, maxCapacity, and capacityIncrement arguments determine how many database connections the pool contains and when they are created. When the connection pool is first created, WebLogic Server creates initialCapacity connections. When all of the connections are in use and another one is requested, WebLogic Server creates capacityIncrement new connections and adds them to the pool. The pool will never have more than maxCapacity connections.

The props argument supplies properties that are passed to the DriverManager.getConnection() method when a connection is created.

The demoPool property does not use all of the available connection pool configuration options. You can find the entire set of connection pool properties in Using WebLogic JDBC.

You can view and change connection pool properties in the WebLogic Console. The Console displays usage statistics for pools, which are useful for tuning connection pool properties.

Using connection pools in JDBC clients

Once a connection pool is set up in WebLogic Server, clients get a connection by specifying the pool with the weblogic.t3.connectionPoolID property.

In this section, we will create a simple Java client, booksT3pool.java. This example obtains a JDBC Connection object, creates a Statement and executes some SQL commands on it, including some DDL (create table and drop table) and DML (insert and select). It shows how to process the results by getting a JDBC ResultSet object from the Statement. This example also uses a JDBC PreparedStatement to perform the SQL insert commands. A PreparedStatement can be more efficient on the database server for repeated commands because it allows the server to parse and optimize the statement once instead of each time it is executed.

Listing 2-8 booksT3pool.java


package examples.intro;

import java.sql.*;
import java.util.Properties;
import weblogic.common.*;

public class booksT3pool {

public static void main(String argv[]) throws Exception {

T3Client t3 = null;
Connection conn = null;
Properties t3props = new Properties();


try {
t3 = new T3Client("t3://localhost:7001");
t3.connect();
t3props.put("weblogic.t3", t3);
t3props.put("weblogic.t3.connectionPoolID", "demoPool");
Class.forName("weblogic.jdbc.t3.Driver").newInstance();
conn = DriverManager.getConnection("jdbc:weblogic:t3",
t3props);

Statement s = conn.createStatement();

try {
// First, drop the "books" table, in case it
// already exists. If it does not, then this
// statement will get a warning that can be ignored.
System.out.println("Dropping table books.");
s.execute("drop table books");
}
catch ( SQLException se ) {;}


// Now, create the "books" table and insert some data.
System.out.println("Creating table books.");
s.execute("create table books (title varchar(40), " +
"author varchar(40), publisher varchar(40),"+
"isbn varchar(20))");

// Use a PreparedStatement to insert rows in the table.
System.out.println("Loading table.");
PreparedStatement ps = conn.prepareStatement(
"insert into books " +
"(title,author,publisher,isbn) " +
" values (?, ?, ?, ?)");

ps.setString(1, "Java in a Nutshell");
ps.setString(2, "David Flanagan");
ps.setString(3, "O'Reilly");
ps.setString(4, "1-56592-262-X");
ps.execute();

ps.setString(1, "Java Examples in a Nutshell");
// Next two columns are the same as the previous
// insert statement, so they don't have to be set again.
// ps.setString(2, "David Flanagan");
// ps.setString(3, "O'Reilly");
ps.setString(4, "1-56592-371-5");
ps.execute();

ps.setString(1, "Design Patterns");
ps.setString(2, "Gamma, Helm, Johnson, Vlissides");
ps.setString(3, "Addison-Wesley");
ps.setString(4, "0-201-63361-2");
ps.execute();

System.out.println("Retrieving rows.");
s.execute("select * from books");

ResultSet rs = s.getResultSet();
while (rs.next()) {
System.out.println(rs.getString("title") + "\n\t" +
rs.getString("author") + "\n\t" +
rs.getString("publisher") + "\n\t" +
rs.getString("isbn"));
}
rs.close();
s.close();
ps.close();
} finally {
try {conn.close();} catch (Exception e) {;}
try {t3.disconnect();} catch (Exception e) {;}
}
}
}


Running the booksT3pool example

To run the booksT3pool example, follow these steps:

  1. Set up your development environment as described in Setting your development environment.

  2. Edit the weblogic.properties file and uncomment the weblogic.jdbc.connectionPool.demoPool property and the weblogic.allow.reserve.weblogic.jdbc.connectionPool.demoPool properties.

  3. Start WebLogic Server.

  4. In your development shell, change to the directory where you copied the examples.

  5. Compile the booksT3pool.java program.

    Windows NT:

    javac -d %CLIENT_CLASSES% booksT3pool.java

    UNIX:

    javac -d $CLIENT_CLASSES booksT3pool.java

  6. Run the example with this command:

    java examples.intro.booksT3pool

More about WebLogic JDBC

There are many JDBC features and developer issues that could not be presented in this introduction. Some other JDBC features are illustrated in other sections of this document. For example, the SqlServlet.java example in the Servlets section shows how to use a JDBC connection pool in a server-side class, and how to access database metadata from a ResultSet.

Here are some online documents where you can find out more about JDBC and additional WebLogic JDBC features:

Documentation for WebLogic JDBC drivers:

JNDI (Java Naming and Directory Interface)

JNDI is a standard Java interface for accessing distributed services and data by name. It provides a portable, unified interface for services that need naming and directory services.

JNDI provides a standard way to access WebLogic Server services other than HTTP Servlets and JSP pages. WebLogic Server maps objects such as Enterprise JavaBeans, Remote Methods, and JMS Queues and Topics to names in a JNDI naming tree. JNDI allows you to look up a service by name.

JNDI is defined independently of any specific naming or directory service implementation, but you can use it to access multiple, different naming and directory services such as LDAP, NDS, and NIS (YP).

JNDI distinguishes between naming services and directory services:

WebLogic JNDI is a multitier JNDI implementation. Through WebLogic JNDI, a client can access any JNDI service provider that is accessible to WebLogic Server. This gives clients seamless access to services and data without any knowledge of physical location.

Using JNDI

To access a directory or naming service, you get a JNDI InitialContext object, which provides methods for binding and retrieving objects in a JNDI provider's namespace. To get the InitialContext, you define properties in a Hashtable, and then pass it to the getInitialContext() method. The javax.naming.Context class (which is the parent of the InitialContext class) provides constants for the property names.

The properties you can set for WebLogic Server are:

Context.PROVIDER_URL
Use this property to specify the URL of the JNDI provider. The default is t3://localhost:7001, which is correct if you are running WebLogic Server on the same machine as the client and you are have not changed the listen port in the default weblogic.properties file.

Context.INITIAL_CONTEXT_FACTORY
Specifies the fully-qualified name of the class that creates the InitialContext object. For WebLogic Server, this is weblogic.jndi.WLInitialContextFactory. This is the only property that is required.

Context.SECURITY_PRINCIPAL
Use this property to supply the name of the WebLogic Server user. When you use JNDI to look up services in the WebLogic Server, you establish a T3 connection to the server that carries the identity of the WebLogic Server user. If any of the services you want to use are protected with Access Control Lists (ACLs), you need to supply the username of a WebLogic user who has the proper permissions. If you do not set this property, the connection defaults to the WebLogic "guest" user.

Context.SECURITY_CREDENTIALS
Use this property to supply the password for the WebLogic user you specified.

This example shows how to construct a Hashtable using all of these properties.

Hashtable ht = new Hashtable(4);
ht.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
ht.put(Context.PROVIDER_URL, "t3://localhost:7001");
ht.put(Context.SECURITY_PRINCIPAL, "system");
ht.put(Context.SECURITY_CREDENTIALS, "gumby1234");

You create the InitialContext using the Hashtable as the argument to the InitialContext constructor:.

Context ctx = new InitialContext(ht);

Once a client has an InitialContext, it has access to all of the WebLogic Server services available through JNDI. The client can also get a Context for another network-accessible JNDI provider to use its services. For example, a client can get an InitialContext from WebLogic Server and then use it to get a Context for an LDAP service at some other location on the network.

Using WebLogic JNDI classes

WebLogic extends the Sun JNDI API to make it easier to use JNDI with WebLogic Server and to access WebLogic Server features such as clustering and SSL authentication.

The weblogic.jndi classes include weblogic.jndi.Environment, weblogic.jndi.WLContext and weblogic.jndi.WLInitialContextFactory classes and provide support for WebLogic-specific features.

WLInitialContextFactory

The weblogic.jndi.WLInitialContextFacory class creates an InitialContext object. When connecting to a WebLogic Server, this class creates an InitialContext that implements the weblogic.jndi.WLContext interface. The name of this class must be specified in the Context.INTITIAL_CONTEXT_FACTORY as described in the previous section.

WLContext interface

The weblogic.jndi.WLContext interface defines additional WebLogic-specific properties that can be set in the Hashtable passed to the getInitialContext() method.

Environment class

The weblogic.jndi.Environment class is a convenience class that you can use in place of the Hashtable use in creating a JNDI connection. It provides type-safe methods to set and get common JNDI properties and a convenient way to create a new initial context.

More about JNDI

You can learn more about WebLogic JNDI in Using WebLogic JNDI.

Go to the Sun JNDI page to find the latest news on JNDI and additional JNDI documentation and examples.

RMI (Remote Method Invocation)

RMI provides tools and the framework to allow a class running in one JVM to execute methods on a class in another JVM. With RMI, a "server" JVM defines objects that can be executed remotely by "client" JVMs. Clients invoke methods on remote objects exactly as if they were local objects. RMI handles the details of transporting requests, arguments, and results between the JVMs.

With RMI, the client-server paradigm only serves to clarify the defining (server) JVM and the invoking (client) JVM. The remote object "server" can be WebLogic Server or any other Java application. The "client" invoking a remote object could be WebLogic Server or another Java client. For example, one of the sample programs in the WebLogic Server distribution is an applet that defines a remote object that WebLogic Server executes remotely-a pattern that looks very much like a callback design.

To use WebLogic RMI, you write two Java classes: an interface and an implementation of that interface. The interface extends weblogic.rmi.remote. It defines each method that can be executed remotely by the client. The second class implements each method in the interface, providing the server-side code that the client executes remotely.

After you have compiled the interface and its implementation, you run the WebLogic RMI compiler to create a stub and a skeleton. The stub is the client's reference to the remote class. The client gets an instance of the stub and uses it as if it were a local object. The skeleton, which resides on the server, accepts requests from the stub and calls the remote class on the client's behalf. The generated stub and skeleton take care of the communication details between the client and server. Since they are generated automatically, you only have to be concerned with writing the interface and implementation classes for the remote object. Low-level details are hidden by the RMI framework.

A remote class is installed in a registry so that the client can look it up and retrieve the stub. The Sun java.rmi.Naming package can be used to register the class. With WebLogic RMI, we recommend that you use WebLogic JNDI to register a remote class. weblogic.rmi.Naming is provided for compatibility.

RMI is standard in JDK 1.1. However, WebLogic Server provides a drop-in replacement for Sun's RMI that allows RMI network traffic to be mixed with other traffic over a single TCP/IP connection. The WebLogic T3 protocol multiplexes all data exchanged between WebLogic Server and a client. WebLogic RMI also adds some additional information to RMI client stubs to support WebLogic Server clusters. We call these enhanced RMI stubs "smart stubs."

More about RMI

EJB (Enterprise JavaBeans)

The Enterprise JavaBeans specification defines a model for component-based applications in Java. WebLogic Server version 4.5 implements the 1.0 EJB specification, including nearly all optional features.

Like JavaBeans, EJBs are reusable software components that can be assembled into applications. But EJBs execute in an EJB Server, WebLogic Server in this case, and they encapsulate business logic instead of GUI objects.

EJB defines separate, independent roles and responsibilities for:

An EJB developer creates an EJB by defining a public EJB interface for the bean and implementing the interfaces for the desired EJB characteristics.

An application developer uses EJBs by calling standard JNDI and EJB methods and the EJB's public interface methods.

An EJB container, such as WebLogic Server, manages an EJB through its life cycle and provides run-time services, such as caching, transaction management, persistence, and tools that support EJB deployment.

System administrators deploy EJBs in the servers they manage. They configure runtime properties for each EJB by modifying a deployment descriptor. The deployment descriptor supplies the EJB server with the information it requires to host the EJB. The deployment descriptor also allows the system administrator to define permissions on an EJB and to configure resources used by an EJB.

The EJB specification establishes "contracts" that define the services provided by each role. Be sure to read the EJB specification before you begin developing or using EJBs. By defining the roles and responsibilities for creating, deploying, hosting, and accessing EJBs, the EJB component model enables you to create your own EBJs or obtain EJBs from other sources and easily incorporate them into applications.

EJBs depend upon, and utilize, other J2EE technologies. For example, you use JNDI to access an instance of an EJB in applications, such as HTTP Servlets or JSP pages. EJBs can use JDBC for persistence and they can use JTA to participate in transactions involving other transactional J2EE technologies such as JMS. They can use RMI or IIOP (CORBA) to operate in a distributed environment.

Types of EJBs

There are two types of EJBs: session beans and entity beans. Each of these has subtypes, which are described in the following sections.

Session beans

A session bean is a transient EJB instance that serves a single client. The EJB container creates a session bean at a client's request and maintains the bean as long as the client maintains its connection to the bean. Sessions beans are not persistent. Session beans tend to implement procedural logic; they embody actions more than data.

A session bean can be stateless or stateful. Stateless session beans maintain no client-specific state between calls and can be used by any client. They can be used to provide access to services that do not depend on the context of a session, such as sending a document to a printer or retrieving non-updateable data into an application.

A stateful session bean maintains some state on behalf of a specific client. Stateful session beans can be used to manage a process, such as assembling an order or routing a document through a workflow process. With the ability to accumulate and maintain state through multiple interactions with a client, session beans are often the controlling objects in an application. Since they are not persistent, session beans must complete their work in a single session and use JDBC, JMS, or entity beans to record the work permanently.

Entity beans

An entity bean represents a dataful object, such as a customer, an account, or an inventory item. Entity beans contain data values and methods to act upon those values. The values are saved in a database (using JDBC) or in a file (as a serializable Java object), or in some other data store. Entity beans can participate in transactions involving other EJBs and transactional services, such as JMS.

Entity beans are often mapped to objects in databases. An entity bean could represent a row in a table, a single column in a row, or an entire table or query result. Application requirements determine how beans are mapped to database objects.

An entity bean can employ bean-managed persistence, where the bean contains code to retrieve and save persistent values, or container-managed persistence, where the EJB container loads and saves values on behalf of the bean. With container-managed persistence, the WebLogic EJB compiler can generate JDBC support classes to map an entity bean to a row in a database. Other container-managed persistence mechanisms are available. For example, TOPLink for BEA WebLogic Server, from The Object People, provides persistence for an object relational database.

Entity beans may be shared by many clients and applications. An instance of an entity bean can be created at the request of some client, but it does not disappear when that client disconnects. It continues to live as long as any client is actively using it. When the bean is no longer in use, the EJB container may passivate it-remove the live instance from the server. To minimize accesses to the persistence store, and to optimize server performance, the EJB server administrator can tune the frequency of passivation in a bean's deployment descriptor. For example, the system administrator can specify the maximum number of bean instances to cache and how long to delay passivation when the bean is no longer in use. By configuring each bean independently, the system administrator can tune a WebLogic Server for the combination of applications that share it.

Developing EJBs

Developing an EJB involves implementing interfaces from the javax.ejb package and defining and implementing the bean's public interface. The EJB specification tells you what classes, interfaces, and methods are required for each type of bean. The specification also contains rules that must be followed to ensure that the bean can be deployed in any compliant EJB server.

Some EJB code is generated with tools provided with the EJB server. The WebLogic EJB compiler, weblogic.ejbc, generates this code for WebLogic Server. WebLogic Server also includes weblogic.ejb.utils.DDCreator, a utility that generates a serialized deployment descriptor from a text file description, and weblogic.ejb.utils.ComplianceChecker, which checks that a packaged EJB is complete and compliant.

The Emp example shows how to build a JDBC container-managed entity bean for an existing database table. This example uses the Emp table, which is in the Cloudscape demo database included in the WebLogic Server distribution.

The Emp table has the following SQL definition:

create table emp (
empno int not null,
ename varchar(10),
job varchar(9),
mgr int,
hiredate date,
sal float,
comm float,
deptno int
)

The Emp Enterprise JavaBean example includes these source files:

EmpBeanHome.java extends javax.ejb.EJBHome
Defines the home interface for the Emp bean, including one create() signature, and four finder methods.

Emp.java extends javax.ejb.EJBObject
The public interface for the Emp EJB. It defines a get method for each field, a set method for each field except the primary key (empno), and an htmlToPrint() method that returns a String representing an Emp EJB instance in HTML.

EmpBean.java implements javax.ejb.EntityBean
Contains the implementation for the Emp EJB and the EntityBean methods required by the EJB specification. It declares public variables corresponding to the Emp database table columns. It implements the required ejbCreate() method and overrides several life cycle methods, which are defined in the javax.ejb.EntityBean interface. Finally, it implements each method defined by the Emp interface.

EmpBeanPK.java implements java.io.Serializable
The primary key class for the Emp EJB. The primary key for this database table is the empno column. This class defines the default constructor and a constructor that sets the primary key field for an instance of this class.

DeploymentDescriptor.txt
A text file containing EJB deployment information for this bean. The weblogic.ejb.utils.DDCreator utility generates a Java resource file from this file. The resource file, with a .ser extension, is stored in the jar file that contains the bean's classes. WebLogic Server uses the resource file to deploy the bean.

manifest
A file stored in the EJB jar file with the compiled Emp classes. The format of this file is specified in the EJB 1.0 specification. It tells WebLogic Server that the jar file contains an EJB and it specifies the resource file that contains the bean's deployment information.

Remote interface

The remote interface defines a public interface for an EJB. The remote interface for the Emp bean is defined in the Emp.java file:

Listing 2-9 Emp.java


package examples.intro;

import javax.ejb.*;
import java.rmi.RemoteException;
import java.sql.Date;

public interface Emp extends EJBObject {

public int getEmpno() throws RemoteException;
public String getEname() throws RemoteException;
public String getJob() throws RemoteException;
public int getMgr() throws RemoteException;
public Date getHiredate() throws RemoteException;
public double getSal() throws RemoteException;
public double getComm() throws RemoteException;
public int getDeptno() throws RemoteException;

public void setEname(String newEname) throws RemoteException;
public void setJob(String newJob) throws RemoteException;
public void setMgr(int newMgr) throws RemoteException;
public void setHiredate(Date newHiredate) throws RemoteException;
public void setSal(double newSal) throws RemoteException;
public void setComm(double newComm) throws RemoteException;
public void setDeptno(int newDeptno) throws RemoteException;

public String htmlToPrint() throws RemoteException;
}


The interface defines one get method and one set method for each field. The data types correspond to the JDBC data types of the database table. The htmlToPrint() method returns a String with the employee's data coded as a row in an HTML table. The string can be directed to the output of an HTTP Servlet or JSP page.

Implementation class

The implementation for the Emp bean is in the EmpBean.java file. The bean implements the javax.ejb.EntityBean interface, which defines several methods that an entity bean must implement. These methods are called by the EJB container (WebLogic Server), at various times during the bean's life cycle. The EmpBean class also implements the methods defined by the Emp interface.

Listing 2-10 EmpBean.java


package examples.intro;

import javax.ejb.*;
import java.rmi.RemoteException;
import java.util.*;
import java.sql.Date;

public class EmpBean implements EntityBean {

protected EntityContext entityContext;

public int empno;
public String ename;
public String job;
public int mgr;
public Date hiredate;
public double sal;
public double comm;
public int deptno;

public void ejbCreate(int v_empno, String v_ename, String v_job,
int v_mgr, Date v_hiredate, double v_sal,
double v_comm, int v_deptno)
throws RemoteException {
empno = v_empno;
ename = v_ename;
job = v_job;
mgr = v_mgr;
hiredate = v_hiredate;
sal = v_sal;
comm = v_comm;
deptno = v_deptno;
}

public void ejbPostCreate(int v_empno, String v_ename,
String v_job,int v_mgr, Date v_hiredate,
double v_sal, double v_comm, int v_deptno)
throws RemoteException
{
// do nothing
}

public void ejbActivate() throws RemoteException {
// do nothing
}

public void ejbLoad() throws RemoteException {
// do nothing
}

public void ejbPassivate() throws RemoteException {
// do nothing
}

public void ejbRemove() throws RemoteException {
// do nothing
}

public void ejbStore() throws RemoteException {
// do nothing
}

public void setEntityContext(EntityContext ctx)
throws RemoteException {
entityContext = ctx;
}

public void unsetEntityContext() {
entityContext=null;
}

public int getEmpno() throws RemoteException {
return empno;
}

public String getEname() throws RemoteException {
return ename;
}
.
. // methods omitted from listing
.
public void setEname(String newEname) throws RemoteException {
ename = newEname;
}

public void setJob(String newJob) throws RemoteException {
job = newJob;
}
.
. // methods omitted from listing
.
public String htmlToPrint() throws RemoteException {
String work = "<tr>" +
"<td>" + getEmpno() + "</td>" +
"<td>" + getEname() + "</td>" +
"<td>" + getJob() + "</td>" +
"<td>" + getMgr() + "</td>" +
"<td>" + getHiredate() + "</td>" +
"<td>" + getSal() + "</td>" +
"<td>" + getComm() + "</td>" +
"<td>" + getDeptno() + "</td></tr>";
return work;
}
}


EmpBean.java implements methods from the javax.ejb.EntityBean interface. WebLogic Server calls these methods when an EJB moves from one state to another, for example when the bean moves from active to passive state or to and from the database. These methods do not have any specified behaviors; they are provided so you can perform any actions required when an event changes the bean's status.

In a bean-managed persistence entity bean, the ejbLoad() and ejbStore() methods contain the code to load or save a bean. In Emp, these are notification callbacks that are called after loading or before storing the bean to the database.

An ejbCreate() method is called when a bean is created. You must supply an ejbCreate() method to match each create() method you define in the bean's home interface. This example has one ejbCreate() method that sets all of the fields in the bean. A matching postCreate() method is called after the bean is created to allow the bean to complete any initialization activity required.

The get methods simply return the value of a field. The set methods change the field value.

Home interface

The home interface defines create methods, which behave like constructors for the bean, and finder methods, which the container calls with application-supplied criteria to retrieve a bean instance or an enumeration of bean instances from the persistence store. The home interface for the Emp bean is in the EmpBeanHome.java file:

Listing 2-11 EmpBeanHome.java


package examples.intro;
import javax.ejb.*;
import java.rmi.RemoteException;
import java.sql.Date;

public interface EmpBeanHome extends EJBHome {

public Emp create(int empno, String ename, String job, int mgr,
Date hiredate, double sal, double comm,
int deptno)
throws RemoteException, CreateException;

public Emp findByPrimaryKey(EmpBeanPK pk)
throws RemoteException, FinderException;

public Emp findByEmpno(int empno)
throws RemoteException, FinderException;

public Enumeration findByName(String name)
throws RemoteException, FinderException;

public Enumeration findByNameLike(String name)
throws RemoteException, FinderException;

}


This interface defines one create() method and four finder methods. The findByPrimaryKey() finder method is required and takes an instance of the bean's primary key class as its argument. The findByEmpNo() method is equivalent to the findByPrimaryKey() method. It returns a single Emp instance because the empno field is unique. The findByName() and findByNameLike() finders return Enumerations, since either method could match more than one employee.

In Emp, the JDBC code that implements these methods is generated by the WebLogic EJB compiler using this interface and definitions in the bean's deployment descriptor.

Primary key class

Entity beans must have a unique primary key to distinguish bean instances. The primary key is constructed from one or more fields and is described in a class that implements java.io.Serializable. The fields that represent the primary key must be declared public members of the primary key class and the class must have a public default constructor (with no arguments). The home interface includes a finder method, findByPrimaryKey(), which uses an instance of this class to retrieve a specific bean.

The primary key class for the Emp bean is in the EmpBeanPK.java file:

Listing 2-12 EmpBeanPK.java


package examples.intro;

import javax.ejb.*;
import java.rmi.RemoteException;
import java.util.*;
import java.lang.Integer;

public class EmpBeanPK implements java.io.Serializable {

public int empno;
public EmpBeanPK(int empno) {this.empno = empno;}

public EmpBeanPK() { }
public String toString() {
Integer n = new Integer(empno);
return n.toString();
}
}


Deployment descriptor

A deployment descriptor contains information that describes the structure of a bean, and application and environment-specific attributes of a bean.

Ultimately, the deployment descriptor is a serialized javax.ejb.deployment.DeploymentDescriptor object. With WebLogic Server, you can first edit the deployment descriptor in a text file and then use the weblogic.ejb.utils.DDCreator utility to translate the file to a serialized Java object. You can also use the WebLogic EJB Deployment wizard to create and edit deployment descriptors.

The deployment descriptor identifies the Java classes that implement the bean, the name used to bind the bean in the WebLogic Server JNDI tree, transaction requirements, security properties, caching and passivation attributes and, for container managed-entity beans, attributes to map the bean to database objects and finder methods.

See the javax.ejb.deployment javadocs for details about the contents of the deployment descriptor.

The text file representation of the deployment descriptor for the Emp bean is in the DeploymentDescriptor.txt file:

Listing 2-13 DeploymentDescriptor.txt


(EntityDescriptor
beanHomeName EmpBeanHome
enterpriseBeanClassName examples.intro.EmpBean
homeInterfaceClassName examples.intro.EmpBeanHome
remoteInterfaceClassName examples.intro.Emp

isReentrant false

(accessControlEntries
DEFAULT [everyone]
);

(controlDescriptors
(DEFAULT
isolationLevel TRANSACTION_SERIALIZABLE
transactionAttribute TX_REQUIRED
runAsMode CLIENT_IDENTITY
);
);

(environmentProperties
maxBeansInFreePool 20
maxBeansInCache 100
idleTimeoutSeconds 10
delayUpdatesUntilEndOfTx true
(finderDescriptors
"findByEmpno(int empno)" "(= empno $empno)"
"findByName(String name)" "(= ename $name)"
"findByNameLike(String name)" "(like ename $name)"
);

(persistentStoreProperties
persistentStoreType jdbc
(jdbc
tableName emp
dbIsShared true
poolName demoPool
(attributeMap
empno EMPNO
ename ENAME
job JOB
mgr MGR
hiredate HIREDATE
sal SAL
comm COMM
deptno DEPTNO
);
);
);
);
primaryKeyClassName examples.intro.EmpBeanPK
containerManagedFields *
);


Manifest file

EJBs are packaged in a jar file that must contain the deployment descriptor, the EJB classes, and a manifest file. For each EJB packaged in a jar file, the manifest file has one entry. Here are the contents of the manifest file for the Emp bean:

Listing 2-14 manifest file


Manifest-Version: 1.0

Name: examples/intro/EmpBeanDD.ser
Enterprise-Bean: True


Each entry in the manifest file gives the name of a deployment descriptor and identifies the entry as an EJB.

Running the Emp example

  1. Set up your development environment as described in Setting your development environment.

  2. Change to the directory where you copied the examples, and compile the Java source files. Enter this command on a single line:

    javac -d . Emp.java EmpBean.java EmpBeanPK.java 
    EmpBeanHome.java

    This command compiles the Emp bean into the ./examples/intro directory-a directory used to "stage" the EJB.

    An enterprise bean is packaged in a jar file. To create the jar file, you create a directory structure with all of the files you want to include. Then when you run the jar program, the directory structure is duplicated in the jar file.

  3. Compile the Java source files again, this time into the directory specified by the CLIENT_CLASSES environment variable.

    Windows NT:

    javac -d %CLIENT_CLASSES% Emp.java EmpBean.java EmpBeanPK.java 
    EmpBeanHome.java

    UNIX:

    javac -d $CLIENT_CLASSES Emp.java EmpBean.java EmpBeanPK.java 
    EmpBeanHome.java

    The first javac command creates the classes in a staging directory that is used to create the bean's jar file. The second javac command compiles the classes in a directory in the classpath so that the WebLogic EJB compiler can find them later.

  4. Generate a deployment descriptor for the bean with the weblogic.ejb.utils.DDCreator utility. Again, enter this command all on one line.

    java weblogic.ejb.utils.DDCreator 
    -d examples/intro DeploymentDescriptor.txt

    This generates the file examples/intro/EmpBeanDD.ser.

  5. Run the WebLogic EJB compiler, specifying the EmpBeanDD.ser file you created in the previous step.

    java weblogic.ebjc -d . examples/intro/EmpBeanDD.ser

    The EJB compiler generates several Java class files in the ./examples/intro directory.

  6. Create a jar file containing the bean's classes and manifest file:

    jar cmf manifest Emp.jar examples

    This command creates the Emp.jar file containing all of the files in the examples subdirectory plus the manifest file from the current directory.

  7. Copy the Emp.jar file into the myserver directory of your WebLogic installation.

  8. Edit the weblogic.properties file and find the weblogic.ejb.deploy property. Add the full path for the Emp.jar file to this property. For example, if you installed WebLogic Server in the c:\weblogic directory, this property would be:

       weblogic.ejb.deploy=\
    c:/weblogic/myserver/Emp.jar

  9. Start (or restart) WebLogic Server.

You should see a message in the WebLogic Server log to tell you that the bean has been deployed:

Thu Oct 21 08:55:39 PDT 1999:<I> <EJB> EJB home interface: 'examples.intro.EmpBeanHome' deployed bound to the JNDI name: 'EmpBeanHome'

The next section shows how to use the Emp bean in an application.

Using the bean in an application

The EmpQuery.jsp JSP page is a browser-based lookup form that lets you query the Emp table using the Emp bean's finder methods. It demonstrates how to look up a bean with JNDI, how to call the finder methods, and how to use the bean's htmlToPrint() method to include the bean's fields in an HTML table.

Here is a listing of the EmpQuery.jsp file:

Listing 2-15 EmpQuery.jsp


<!doctype html public "-//w3c/dtd HTML 4.0//en">
<html>
<head><title>Employee lookup</title></head>

<%@ page
info="EmpBean lookup example"
contentType="text/html"
import="java.lang.Integer, javax.naming.*, javax.ejb.*,
weblogic.common.*, examples.intro.Emp,
examples.intro.EmpBeanHome"
%>
<body>
<font face="Helvetica">
<h1><font color="#DB1260">Employee Lookup</font></h1>

<hr>
<form action=<%= request.getRequestURI() %> method="post">
<table border=0 cellspacing=5 align=center>
<tr>
<td>Search by</td>
<td><select name=searchType>
<option value=1>Employee number</option>
<option selected value=2>Employee name</option>
<option value=3>Name like</option>
</select>
</td>
</tr>
<tr>
<td>Search for</td>
<td><input type=text name=criteria size=10 maxlength=10></td>
</tr>
<tr>
<td align=center colspan=2><input type=submit value="Go">
</td>
</tr>
</table>
</form>
<p>

<%

if (request.getParameter("searchType") != null
&& request.getParameter("criteria") != null)
{
int searchType = Integer.parseInt(request.getParameter("searchType"));
String criteria = request.getParameter("criteria");
Emp emp = null;
Enumeration emps = null;
try {
Context ctx = new InitialContext();
EmpBeanHome home = (EmpBeanHome)
ctx.lookup("EmpBeanHome");
switch (searchType) {
case 1: // by employee number
emp = (Emp) home.findByEmpno(Integer.parseInt(criteria));
break;
case 2: // by Employee name
emps = home.findByName(criteria.toUpperCase());
break;
case 3: // by name like
emps = home.findByNameLike(criteria.toUpperCase());
break;
default:
}

if (emp != null || emps != null) {
// we have data
%>

<hr>
<h2>Search Results</h2>
<table border=1 align=center>
<tr>
<th>Employee number</th>
<th>Name</th>
<th>Title</th>
<th>Manager</th>
<th>Hire date</th>
<th>Salary</th>
<th>Commission</th>
<th>Department</th>
</tr>

<%
}
if (emp != null) {
out.print(emp.htmlToPrint());
}
else {
while (emps.hasMoreElements()) {
emp = (Emp) emps.nextElement();
out.print(emp.htmlToPrint());
}
}
%>
</table>

<%
}
catch (NumberFormatException nfe) {
out.print("<p>Error attempting to convert " +
criteria + " to an integer.");
}
catch (Exception e) {
out.print("<p>Exception: " + e.getMessage());
}
}
%>
<p>
<hr>
<font face="Helvetica">
<p>Executed by
<%= application.getServerInfo() %>.<br>
Copyright 1999 (C) BEA Systems, Inc.
All Rights Reserved.
</body>
</html>


You use JNDI to look up the bean's home interface:

Context ctx = new InitialContext();
EmpBeanHome home = (EmpBeanHome) ctx.lookup("EmpBeanHome");

Then, using the home interface, you can retrieve instances by using the bean's finder methods, or you could create a new instance using the create() method of the home interface. This example uses the findByEmpno(), findByName(), or findByNameLike() methods to retrieve beans, depending on user's browser entries.

The findByEmpno() method returns a single instance of an Emp bean while the findByName() and findByNameLike() methods return Enumerations of Emp beans. In either case, you must cast the returned object to the Emp interface, and then you can use any of the methods defined by that interface.

Running the EmpQuery.jsp example

To try the EmpQuery.jsp example, make sure you have deployed the Emp bean as described in the previous section and that WebLogic Server is running.

  1. Copy EmpQuery.jsp to the myserver/public_html directory of your WebLogic Server installation.

  2. In a browser, enter the URL for EmpQuery.jsp. For example:

    http://localhost:7001/EmpQuery.jsp

    Change the hostname and port number if you are running WebLogic Server on a different computer or at a different listen port.

  3. Choose the type of lookup, enter the value to search for, and click Go.

The "Name Like" search type employs the SQL LIKE clause, which uses the percent sign (%) as a wildcard. For example, to find all employees with names starting with `A', enter "A%" in the "Search for" field. You can list all employees by entering "%" in the "Search for" field with a "Name like" search type.

More about EJB

The Sun Enterprise JavaBeans 1.0 specification is required reading for EJB developers. You can find javadocs for the javax.ejb interfaces and classes on the same web page.

Read the WebLogic EJB developers guide, Using WebLogic EJB for more about creating and deploying EJBs in WebLogic Server.

You can get help using the WebLogic EJB utilities here:

JMS (Java Message Service)

Java Message Service (JMS) allows Java programs to exchange messages with other Java programs sharing a messaging system. A messaging system accepts messages from "producer" clients and delivers them to "consumer" clients.

Messaging systems, sometimes called Message-Oriented Middleware (MOM), enable Java clients to use their services by supplying a Java layer called a JMS Provider, which implements JMS for the specific product.

WebLogic JMS implements the JMS specification version 1.0.1, which is online at http://www.javasoft.com/products/jms/docs.html. WebLogic JMS includes a full-featured messaging system, which can be configured by setting properties in the weblogic.properties file, from the WebLogic Console, or programmatically, using the JMS interfaces.

You can use WebLogic JMS with the other WebLogic Server APIs and facilities, such as Enterprise Java Beans, JDBC connection pools, HTTP Servlets, JSP pages and RMI. JMS operations can participate in transactions with other Java APIs that use the Java Transaction API.

JMS messaging models

JMS supports two messaging models: point-to-point (PTP) and publish/subscribe. The terms "message producer" and "message consumer" describe clients that send and receive messages in either of the models, although each model has its own specific terms for producers and consumers.

Point-to-point messaging

The point-to-point messaging model is based on message Queues. A QueueSender (producer) sends a message to a specified Queue. A QueueReceiver (consumer) receives messages from the Queue. A Queue can have multiple QueueSenders and QueueReceivers, but an individual message can only be delivered to one QueueReceiver. If multiple QueueReceivers are listening for messages on a Queue, WebLogic JMS determines which will receive the next message. If no QueueReceivers are listening on the Queue, messages remain in the Queue until a QueueReceiver attaches to the Queue.

Publish/subscribe messaging

The publish/subscribe messaging model is organized around Topics. TopicPublishers (producers) send messages to a Topic. TopicSubscribers (consumers) retrieve messages from a Topic. Unlike the point-to-point model, many TopicSubscribers can receive the same message.

A durable subscriber is a feature of publish/subscribe messaging. A durable subscriber allows you to create a named TopicSubscriber, usually associated with a user or application. JMS retains messages until all TopicSubscribers have received them.

JMS persistence

Messages can be persistent or non-persistent. WebLogic JMS writes persistent messages to a database via a JDBC connection pool you assign to JMS in the weblogic.properties file. A persistent message is not considered sent until it has been stored in the database. Persistent messages are guaranteed to be delivered at least once. Non-persistent messages are not stored in a database and so they may be lost during a failure. A non-persistent message is guaranteed to be delivered at most once.

JMS classes and interfaces

JMS defines several objects that allow you to send and receive messages. JMS objects are subclassed from common parent classes to provide Queue- and Topic-specific versions of the classes.

This section describes the most important JMS classes. See the javax.jms javadocs for complete descriptions of all JMS classes.

ConnectionFactory

You access JMS initially using a ConnectionFactory, which is bound in the WebLogic Server JNDI tree. You look up a QueueConnectionFactory or a TopicConnectionFactory and then use it to create a Connection.

Connection

A Connection (QueueConnection or TopicConnection) manages all of the messaging activity between a JMS client and a JMS provider. It is also a factory for Session objects. A new Connection is stopped-no messages flow until you start the Connection by calling its start() method.

Session

A Session (QueueSession or TopicSession) is a context for producing and consuming messages. Sessions create message consumers and producers and manage the flow of messages, including the ability to group send and receive operations into transactions.

Message producer

A message producer (QueueSender or TopicProducer) transmits messages from a JMS client to a destination (Queue or Topic). A message producer is associated with the destination Queue or Topic when it is created.

Destination

A destination (Queue or Topic) is the object that receives and distributes messages. Destinations are bound in the JNDI tree and can be defined in the weblogic.properties file or in the WebLogic Console. An application can create a TemporaryQueue or TemporaryTopic that exists only as long as the Connection that creates it.

Message consumer

A message consumer (QueueReceiver or TopicSubscriber) receives messages from a destination. A consumer is created by calling the CreateReceiver() or CreateSubscriber() method of the Session. Messages can be received asynchronously by providing a class that implements the MessageListener interface. Messages are forwarded to the onMessage() method of the MessageListener. Messages can be received synchronously by calling a receive() method on the consumer. There are receive() methods that return immediately if no message is waiting, wait for a specified period of time, or wait indefinitely for a message.

Message

JMS messages have three parts. The message header contains fields that the JMS system uses to describe and deliver messages. Message headers are also available to applications. The message properties section contains application-defined properties that can be attached to a message. These properties, and the message headers, can be referenced in selectors, which allow a consumer to filter for messages using criteria resembling an SQL where clause. The message body holds the contents of the message.

JMS supports five types of message bodies. The simplest of these is a TextMessage which holds a single Java String value. A BytesMessage holds a stream of uninterpreted bytes and a StreamMessage holds a stream of Java primitive types. BytesMessage and StreamMessage are written and read using methods similar to those of the java.io.DataOutputStream and java.io.DataInputStream classes. A MapMessage holds name/value pairs similar to a Hashtable. The values can be read randomly by specifying the name, or they can be returned in an Enumeration. An ObjectMessage holds any serializable Java object.

Using JMS

Some JMS objects are "administered," which means they must be configured in WebLogic Server before they can be used by applications. The administered objects are QueueConnectionFactories, TopicConnectionFactories, Queues, and Topics. If you plan to use persistent messages, you must also specify a JDBC connection pool where JMS will store data. If you want to use Enterprise JavaBeans and JMS persistence in the same transaction, the two must use the same connection pool.

JMS administered objects can be configured using the WebLogic Console, but to make them permanent, you define them in the weblogic.properties file. See Configuring WebLogic JMS for instructions for configuring JMS in your WebLogic Server.

Sending messages

To send messages to a JMS Queue or Topic, you use JNDI to look up a ConnectionFactory and the destination Queue or Topic. Using the ConnectionFactory, you create a series of JMS objects, including a Connection, Session, producer (QueueSender or TopicPublisher), and a Message. Then you start the Connection and begin sending messages. Although JMS requires several objects, the send process is very straightforward.

The EmpTrans.java example is an HTTP Servlet that demonstrates how to send messages to a JMS Queue. This example is one part of a rudimentary workflow system. The servlet displays an HTML form and collects data for a transaction against the Emp table. It creates a JMS MapMessage containing the data entered and sends the message to the EmpXactQueue Queue, using the persistent delivery mode so that the message is saved in the JMS database.

The JMS code is in the sendRequest() method of the EmpTrans.java example.

Listing 2-16 EmpTrans.sendRequest()


private boolean sendRequest(
int xactType, int empno,
String ename, String job,
int mgr, java.sql.Date hiredate,
float sal, float comm, int deptno)
{

boolean result = true;

Context ctx = null;
QueueConnectionFactory factory;
QueueConnection qconnection = null;
QueueSession qsession = null;
QueueSender qsender = null;
Queue queue;
MapMessage message;
Hashtable ht = new Hashtable();

ht.put(Context.SECURITY_PRINCIPAL, "guest");
ht.put(Context.SECURITY_CREDENTIALS, "guest");
try {
ctx = new InitialContext(ht);
factory = (QueueConnectionFactory)
ctx.lookup("javax.jms.QueueConnectionFactory");
qconnection = factory.createQueueConnection();
qsession = qconnection.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);

queue = (Queue) ctx.lookup("jms.queue.EmpXactQueue");
qsender = qsession.createSender(queue);
message = qsession.createMapMessage();
qconnection.start();

message.setInt("xactType", xactType);
switch (xactType) {
case 1: // hire
message.setString("ename", ename);
message.setString("job", job);
message.setInt("mgr", mgr);
message.setString("hiredate", hiredate.toString());
message.setFloat("sal", sal);
message.setFloat("comm", comm);
message.setInt("deptno", deptno);
break;
case 2: // termination
message.setInt("empno", empno);
break;
case 3: // salary adjustment
message.setInt("empno", empno);
message.setFloat("sal", sal);
break;
}
// persistent, no priority or expiration
qsender.send(message, DeliveryMode.PERSISTENT,
0, 0);
}
catch (NamingException e) {
result = false;
resultMessage = "JNDI error on " + e.getMessage();
}
catch (JMSException je) {
result = false;
resultMessage = "JMS Error: " + je.getMessage();
}
try { qsender.close(); } catch (Exception e) {};
try { qsession.close(); } catch (Exception e) {};
try { qconnection.close(); } catch (Exception e) {};
try { ctx.close(); } catch (Exception e) {};

return result;
}


You use the same process, but with different object names, to send messages to Topics. See the WebLogic JMS developers guide, Using WebLogic JMS,for more about other message types, using header fields and properties, and other message delivery options.

Receiving messages

Setting up a client to receive JMS messages is nearly the same as setting up to send messages. You look up a ConnectionFactory and a Queue or Topic with JNDI, create a Connection, a Session, and then a message consumer. Then you start the Connection and receive messages.

The EmpQueueReader.java example is the Java client application that reads the messages sent by the EmpTrans HTTP Servlet. This client application reads a message, displays the transaction it holds, and asks if the transaction should be approved. If the user approves the transaction, the application applies the transaction using the Emp Enterprise JavaBean.

Here is the JMS code from the EmpQeueuReader.java example:

Listing 2-17 EmpQueueReader.java


public class EmpQueueReader
{
public final static String JNDI_FACTORY =
"weblogic.jndi.WLInitialContextFactory";
public final static String JMS_FACTORY =
"javax.jms.QueueConnectionFactory";
public final static String QUEUE =
"jms.queue.EmpXactQueue";

static QueueConnectionFactory factory;
static QueueConnection qconnection;
static QueueSession qsession;
static QueueReceiver qreceiver;
static Queue queue;
static Context ctx;
private boolean quit = false;

/**
* Create all the necessary objects for receiving
* messages from a JMS queue.
*/
public static void main(String[] args)
throws NamingException, JMSException, RemoteException
{

Message message;

Hashtable ht = new Hashtable();
ht.put(Context.SECURITY_PRINCIPAL, "guest");
ht.put(Context.SECURITY_CREDENTIALS, "guest");
ht.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
ht.put(Context.PROVIDER_URL, "t3://localhost:7001");

ctx = new InitialContext(ht);
factory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
qconnection = factory.createQueueConnection();
qsession = qconnection.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
queue = (Queue) ctx.lookup(QUEUE);
qreceiver = qsession.createReceiver(queue);
qconnection.start();
while (true) { // get messages until the queue is empty
message = qreceiver.receiveNoWait();
if (message == null)
break;
if (message instanceof MapMessage) {
switch (((MapMessage) message).getInt("xactType")) {
case 1: // hire
doHire((MapMessage) message);
break;
case 2: // termination
doTerm((MapMessage) message);
break;
case 3: // salary adjustment
doSal((MapMessage) message);
break;
}
}
}
qsession.close();
qconnection.close();
}

By using the receiveNoWait() method in a while loop, this example reads and processes all of the messages currently in the Queue and then quits.

Different message types can be mixed on the same Queue or Topic. This example uses instanceof to make sure that it has received a MapMessage. If another message type happens to get into the Queue, this example ignores it.

The switch statement passes the message to a method that handles the transaction type. These methods use the MapMessage getType() methods to retrieve the transaction data from the Message. Then they use the Emp Enterprise JavaBean to add, remove, or update an Employee record. Here is the code that handles hire transactions:

Listing 2-18 EmpQueueReceiver.doHire()


  static void doHire(MapMessage message) 
throws JMSException, NamingException, RemoteException
{
boolean response;
Emp e;

System.out.println("\n\n\n=== Hire request ===");
System.out.println("Employee name ...... : " +
message.getString("ename"));
System.out.println("Job title .......... : " +
message.getString("job"));
System.out.println("Manager ............ : " +
message.getInt("mgr"));
System.out.println("Hire date .......... : " +
message.getString("hiredate"));
System.out.println("Salary ............. : " +
message.getFloat("sal"));
System.out.println("Commission ......... : " +
message.getFloat("comm"));
System.out.println("Department no ...... : " +
message.getInt("deptno"));
System.out.println("===========================");
response = Confirm("Approve this request?");
if (response) {
try {
EmpBeanHome home = (EmpBeanHome) ctx.lookup("EmpBeanHome");
e = home.create(NextEmpno(),
message.getString("ename"),
message.getString("job"),
message.getInt("mgr"),
java.sql.Date.valueOf(
message.getString("hiredate")),
message.getFloat("sal"),
message.getFloat("comm"),
message.getInt("deptno"));
}
catch (CreateException ce) {
System.out.println("EJB CreateException: " +
ce.getMessage());
}
System.out.println("Created new employee.");
}
else
System.out.println("Ignoring request.");
}


Running the EmpTrans example

The EmpTrans example has three parts: the Emp Enterprise JavaBean, an HTTP Servlet, and a Java client application. In addition to setting up these separate applications, you must configure WebLogic Server for JMS.

Notes: This example requires the Emp EJB and depends upon the Emp table in the Cloudscape Demo database that is installed with WebLogic Server. If you want to use a different database, you must edit the EmpTrans.java and EmpQueueReader.java source files. The client application is written to be executed at a command line on the same computer running WebLogic Server. If you want to run the client on a different computer, change the URL in the EmpQueueReader.java file before you compile.

Here are steps to set up and run the EmpTrans example:

  1. Set up your development environment as described in Setting your development environment at http://www.weblogic.com/docs45/techstart/environment.html.

  2. Set up the Emp Enterprise JavaBean. See Running the Emp example for instructions.

  3. Compile the EmpTrans.java servlet:

    Windows NT:

    javac -d %SERVLET_CLASSES% EmpTrans.java

    UNIX:

    javac -d $SERVLET_CLASSES EmpTrans.java

  4. Compile the EmpQueueReader.java client program:

    Windows NT:

    javac -d %CLIENT_CLASSES% EmpQueueReader.java

    UNIX:

    javac -d $CLIENT_CLASSES EmpQueueReader.java

  5. Add or change the following properties in your weblogic.properties file:

  6. Start WebLogic Server.

  7. Load the EmpTrans servlet in a browser with a URL like http://localhost:7001/EmpTrans.

  8. After entering transactions in the EmpTrans servlet, start the EmpQueueReader client application at a command line with a command such as:

    java examples.intro.EmpQueueReader

The EmpTrans servlet displays a web page similar to this:

The output of the EmpQueueReader application is similar to this:

C:\>java examples.intro.EmpQueueReader



=== Hire request ===
Employee name ...... : WHITE
Job title .......... : CLERK
Manager ............ : 7839
Hire date .......... : 1999-10-14
Salary ............. : 2200.0
Commission ......... : 0.0
Department no ...... : 10
===========================
Approve this request? (yes/no) yes
Created new employee.

You can use the SqlServlet servlet to execute a query such as "select * from emp" to see that the Emp bean has updated the Emp database table with your transactions.

More about JMS

To find out more about WebLogic JMS, see Using WebLogic JMS.

Visit the JMS homepage for JMS FAQs, tutorials, and other news.

The javax.jms javadocs are available online. You can also download the JMS specification from Sun.