Oracle8i Enterprise JavaBeans and CORBA Developer's Guide Release 8.1.5 A64683-01 |
|
This section demonstrates a complete example application, including:
This example has a single EJB, which queries an Oracle8i database to get name and salary information about an employee. The example is exactly the same in functionality as the first CORBA example presented in Chapter 3, "Developing CORBA Applications".
In this example, the client code is an application running on a client system. To see how to do an applet example, see the EJBClubMed
example under the basic EJB examples that are shipped with this product.
The first task of the bean provider is to design and code the home and remote interfaces. The home interface specifies how the server will create the bean, using the EJBCreate()
method of the bean implementation. This example creates a stateful session bean that takes no parameters, because there is no initial state for the bean.
(How is it known that the bean is stateful? While this is a design property of the bean, the statefulness of the bean is declared in the deployment descriptor. See "Deployment Steps" for more information.)
The remote interface specifies the methods of the bean. In this example, there is a single method, getEmployee()
, that takes an int as its single parameter, and that returns an EmpRecord
class.
As required by the EJB specification, you must declare that any home interface create()
method throws the javax.ejb.CreateException
and java.rmi.RemoteException
exceptions. When you try to deploy the bean, the deployejb
verifier will exit with an error if this is not the case.
package employee; import javax.ejb.EJBObject; import java.rmi.RemoteException; public interface EmployeeHome extends EJBHome { public Employee create() throws CreateException, RemoteException; }
The remote interface declares that the bean can throw a RemoteException
(required by the specification), and a java.sql.SQLException
, which is particular to this bean. Note that exceptions, such as SQLException
, that are thrown to the bean by JDBC or other methods that it calls are propagated back to client, if the remote interface declares that the bean throws them.
Here is the code for the remote interface for this example EJB:
package employee; import employee.EmpRecord; import javax.ejb.EJBObject; import java.rmi.RemoteException; public interface Employee extends EJBObject { public EmpRecord getEmployee (int empNumber) throws java.sql.SQLException, RemoteException; }
The bean implementation simply fills in the Java code, including appropriate JDBC methods, to perform the work of the getEmployee()
method. Note that the JDBC code opens a default connection, which is the standard way that JDBC code that runs on the Oracle8i server opens a server-side connection. (It is in fact the only way that a JDBC connection can be opened in server-side JDBC code.)
A JDBC prepared statement is used to prepare the query, which has a WHERE clause. Then the setInt()
method is used to associate the empNumber
input parameter for the getEmployee()
method with the '?' placeholder in the prepared statement query. This is no different from the JDBC code that you would write in a client application.
package employeeServer; import java.sql.*; import java.rmi.RemoteException; import javax.ejb.*; public class EmployeeBean implements SessionBean { SessionContext ctx; public EmpRecord getEmployee (int empNumber) throws SQLException, RemoteException { EmpRecord empRec = new EmpRecord(); Connection conn = new oracle.jdbc.driver.OracleDriver().defaultConnection(); PreparedStatement ps = conn.prepareStatement("select ename, sal from emp where empno = ?"); ps.setInt(1, empNumber); ResultSet rset = ps.executeQuery(); if (!rset.next()) throw new RemoteException("no employee with ID " + empNumber); empRec.ename = rset.getString(1); empRec.sal = rset.getFloat(2); empRec.empno = empNumber; ps.close(); return empRec; } public void ejbCreate() throws CreateException, RemoteException { } public void ejbActivate() { } public void ejbPassivate() { } public void ejbRemove() { } public void setSessionContext(SessionContext ctx) { this.ctx = ctx; } }
This remote interface implementation shows the minimum methods required for an EJB implementation. At a minimum, an EJB must implement the following methods, as specified in the javax.ejb.SessionBean
interface:
The EmployeeBean
getEmployee()
method returns an EmpRecord
object, so this object must be defined somewhere in the application. In this example, an EmpRecord
class is included in the same package as the EJB implementation.
The class is declared as public, and must implement the java.io.Serializable
interface, so that it can be passed back to the client by value, as a serialized remote object. The declaration is as follows:
package employee; public class EmpRecord implements java.io.Serializable { public String ename; public int empno; public double sal; }
Note: the java.io.Serializable
interface specifies no methods, it just indicates that the class is serializable. So there is no need to implement extra methods in the EmpRecord
class.
The most convenient way to implement the deployment descriptor for a bean is to write a descriptor file in text form. The EJB deployment tool can read the text form descriptor, parse it, signal parse errors, and then verify that the descriptor itself, and the interface and bean implementation declarations meet the standard. For example, bean implementations and interface specifications must be declared as throwing certain specified exceptions. If they do not, the deployment tool (see deployejb) lists the error(s) and exits.
The text form deployment descriptor is usually stored in a file with a .ejb
extension, though this naming convention is not required. In the EJB examples that are shipped with this product, the deployment descriptors are in the base directory of the example, along with the client application implementations and the Makefile and Windows NT batch files.
Here is the deployment descriptor for this example. For a complete description of the deployment descriptor attributes, see "The Deployment Descriptor".
SessionBean employeeServer.EmployeeBean { BeanHomeName = "test/employeeJDBCBean"; RemoteInterfaceClassName = employee.Employee; HomeInterfaceClassName = employee.EmployeeHome; AllowedIdentities = {SCOTT}; StateManagementType = STATEFUL_SESSION; RunAsMode = CLIENT_IDENTITY; TransactionAttribute = TX_REQUIRED; }
This section shows the client code that can be used to send messages to the example bean described above, and get and print results from it. This client code demonstrates how a client:
The first step with any remote object implementation, whether it's pure RMI, or EJBs, or CORBA, is to find out how to locate a remote object. To get a remote object reference you have to know:
With EJBs, the initial object name is the name of an EJB home interface, and you locate it using the Java Naming and Directory Interface (JNDI). The EJB specification requires that EJB implementations expose a JNDI interface as the means of locating a remote bean.
JNDI is an interface to a naming and directory service. For example, JNDI can be used as an interface to a file system, that you can use to look up directories and the files that they contain. Or, JNDI can be used as an interface to a naming or directory service, for example a directory protocol such as LDAP.
This section presents a short description of JNDI. The EJB specification requires that JNDI be used to provide the interface for locating remote objects by name.
This section of the manual describes only those parts of JNDI that you need to know to write EJB applications for Oracle8i. To obtain the complete JNDI API (and SPI) specifications, see the URLs in "For More Information".
JNDI is supplied by Sun in the packages in javax.naming
, so you must import these packages in your client code:
import javax.naming.*;
For the Oracle8i EJB server, JNDI serves as an interface (SPI driver) to the OMG CosNaming service. But you do not have to know all about CosNaming, or even all about JNDI, to write and deploy EJBs for the Oracle8i server. In fact, to start off all you really need to know is how to use the JNDI methods that are used to get access to permanently-stored home interface objects, and how to set up the environment for the JNDI Context
object.
The remainder of this JNDI section describes the data structures and methods of the javax.naming
package that you will need to access EJB objects.
The very first JNDI call to code is the one that gets a Context
object. The first Context
object that you get is bound to the root naming context of the Oracle8i publishing context. EJB home interfaces are published in the database, arranged in a file system-like hierarchy. See "publish" for more details about publishing EJB home interfaces, and about the Oracle8i published object directory structure.
You get the root naming context by creating a new JNDI InitialContext
, as follows:
Context initialContext = new InitialContext(environment);
The environment
parameter is a Java hashtable. There are six properties that you can set in the hashtable, that are passed to the javax.naming.Context
. The properties are shown in Table 2-1.
See Chapter 4, "Connections and Security", for more information about JNDI and connecting to an Oracle8i instance.
Once you have the "initial references" context, you can invoke its methods to get a reference to an EJB home interface. To do this, you must know the published full pathname of the object, the host system where the object is located, the IIOP port for the listener on that system, and the database system identifier (SID). When you get this information, for example from the EJB deployer, you construct a URL using the following syntax:
<service_name>://<hostname>:<iiop_listener_port>:<SID>/<published_obj_name>
For example, to get a reference to the home interface for a bean that has been published as /test/myEmployee
, on the system whose TCP/IP hostname is myHost
, the listener IIOP port is 2481, and the system identifier (SID) is ORCL
, you construct the URL as follows:
sess_iiop://myHost:2481:ORCL/test/myEmployee
The listener port for IIOP requests is configured in the listener.ora file. The default for Oracle8i is 2481. See the Net8 Administrator's Guide for more information about IIOP configuration information. See also Chapter 4, "Connections and Security" for more information about IIOP connections.
You get the home interface using the lookup()
method on the initial context, passing the URL as the parameter. For example, if the home interface published name is /test/myEmployee
, you would code:
... String ejbURL = "sess_iiop://localhost:2481:ORCL/test/myEmployee"; Hashtable env = new Hashtable(); env.put(javax.naming.Context.URL_PKG_PREFIXES, "oracle.aurora.jndi"); // Tell sess_iiop who the user is env.put(Context.SECURITY_PRINCIPAL, "SCOTT"); // Tell sess_iiop what the password is env.put(Context.SECURITY_CREDENTIALS, "TIGER"); // Tell sess_iiop to use non-SSL login authentication env.put(Context.SECURITY_AUTHENTICATION, ServiceCtx.NON_SSL_LOGIN); // Lookup the URL EmployeeHome home = null; Context ic = new InitialContext(env); home = (EmployeeHome) ic.lookup(ejbURL); ...
Once you have the home interface for the bean, you can invoke one of the bean's create()
methods to instantiate a bean. For example:
Employee testBean = home.create();
Then you can invoke the EJB's methods in the normal way:
int empNumber = 7499; EmpRecord empRec = testBean.getEmployee(empNumber);
Here is the complete code for the client application:
import employee.Employee; import employee.EmployeeHome; import employee.EmpRecord; import oracle.aurora.jndi.sess_iiop.ServiceCtx; import javax.naming.Context; import javax.naming.InitialContext; import java.util.Hashtable; public class Client { public static void main (String [] args) throws Exception { String serviceURL = "sess_iiop://localhost:2481:ORCL"; String objectName = "/test/myEmployee"; int empNumber = 7499; // ALLEN Hashtable env = new Hashtable(); env.put(Context.URL_PKG_PREFIXES, "oracle.aurora.jndi"); env.put(Context.SECURITY_PRINCIPAL, "scott"); env.put(Context.SECURITY_CREDENTIALS, "tiger"); env.put(Context.SECURITY_AUTHENTICATION, ServiceCtx.NON_SSL_LOGIN); Context ic = new InitialContext(env); EmployeeHome home = (EmployeeHome) ic.lookup(serviceURL + objectName); // lookup the bean Employee testBean = home.create(); // create a bean instance EmpRecord empRec = new EmpRecord(); // create a slot for the incoming data empRec = testBean.getEmployee(empNumber); // get the data and print it System.out.println("Employee name is " + empRec.ename); System.out.println("Employee sal is " + empRec.sal); } }