ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Developing Your First Enterprise Beans, Part 1
Pages: 1, 2, 3, 4

cabin.jar: The JAR File

TheJAR file is a platform-independent file format for compressing, packaging, and delivering several files together. Based on the ZIP file format and the ZLIB compression standards, the JAR (Java archive) tool and packages were originally developed to make downloads of Java applets more efficient. As a packaging mechanism, however, the JAR file format is a very convenient way to "shrink-wrap" components and other software for delivery to third parties. In EJB development, a JAR file packages all the classes and interfaces associated with a bean, including the deployment descriptor, into one file.



Creating the JAR file for deployment is easy. Position yourself in the dev directory that is just above the com/titan/cabin directory tree, and execute the following command:

\dev % jar cf cabin.jar com/titan/cabin/*.class META-INF/ejb-jar.xml

F:\..\dev>jar cf cabin.jar com\titan\cabin\*.class META-INF\ejb-jar.xml

You might have to create the META-INF directory first and copy ejb-jar.xml into that directory. The c option tells the jar utility to create a new JAR file that contains the files indicated in subsequent parameters. It also tells the jar utility to stream the resulting JAR file to standard output. The f option tells jar to redirect the standard output to a new file named in the second parameter (cabin.jar). It's important to get the order of the option letters and the command-line parameters to match. You can learn more about the jar utility and the java.util.zip package in Java in a Nutshell by David Flanagan, or Learning Java by Pat Niemeyer and Jonathan Knudsen (both published by O'Reilly).

The jar utility creates the file cabin.jar in the dev directory. If you're interested in looking at the contents of the JAR file, you can use any standard ZIP application (WinZip, PKZIP, etc.), or you can use the command jar tvf cabin.jar.

Creating a CABIN Table in the Database

One of the primary jobs of a deployment tool is mapping entity beans to databases. In the case of the Cabin EJB, we must map its id, name, deckLevel, shipId, and bedCount container-managed fields to some data source. Before proceeding with deployment, you need to set up a database and create a CABIN table. You can use the following standard SQL statement to create a CABIN table that will be consistent with the examples provided in this chapter:

create table CABIN 
(
    ID int primary key NOT NULL, 
    SHIP_ID int, 
    BED_COUNT int, 
    NAME char(30), 
    DECK_LEVEL int
)

This statement creates a CABIN table that has five columns corresponding to the container-managed fields in the CabinBean class. Once the table is created and connectivity to the database is confirmed, you can proceed with the deployment process.

Deploying the Cabin EJB

Deployment is the process of reading the bean's JAR file, changing or adding properties to the deployment descriptor, mapping the bean to the database, defining access control in the security domain, and generating any vendor-specific classes needed to support the bean in the EJB environment. Every EJB server product has its own deployment tools, which may provide a graphical user interface, a set of command-line programs, or both. Graphical deploymentwizards are the easiest deployment tools to use.

A deployment tool reads the JAR file and looks for ejb-jar.xml. In a graphical deployment wizard, the deployment descriptor elements are presented using a set of property sheets similar to those used in environments such as VisualBasic.NET, PowerBuilder, and JBuilder. Figure 4-3 shows the deployment wizard for the J2EE 1.3 SDK (Reference Implementation) server.

Figure 4-3
Figure 4-3. J2EE 1.3 SDK Reference Implementation's deployment wizard

The J2EE Reference Implementation's deployment wizard has fields and panels that match the XML deployment descriptor. You can map security roles to user groups, set the JNDI lookup name, map the container-managed fields to the database, etc. EJB deployment tools provide varying degrees of support for mapping container-managed fields to a data source. Some provide sophisticated graphical user interfaces, while others are simpler and less flexible. Fortunately, mapping the CabinBean's container-managed fields to the CABIN table is a fairly straightforward process. The documentation for your vendor's deployment tool will show you how to create this mapping. Once you have finished the mapping, you can complete the deployment of the Cabin EJB and prepare to access it from the EJB server.

Creating a Client Application

Now that the Cabin EJB has been deployed, we want to access it from a remote client. In this section, we create a remote client that connects to the EJB server, locates the EJB remote home for the Cabin EJB, and creates and interacts with several Cabin EJBs. The following code shows a Java application that creates a new Cabin EJB, sets its name, deckLevel, shipId, and bedCount properties, and then locates it again using its primary key:

package com.titan.cabin;

import com.titan.cabin.CabinHomeRemote;
import com.titan.cabin.CabinRemote;

import javax.naming.InitialContext;
import javax.naming.Context;
import javax.naming.NamingException;
import java.rmi.RemoteException;
import java.util.Properties;
import javax.rmi.PortableRemoteObject;

public class Client_1 {
    public static void main(String [] args) {
        try {
            Context jndiContext = getInitialContext( );
            Object ref = jndiContext.lookup("CabinHomeRemote");
            CabinHomeRemote home = (CabinHomeRemote)
                PortableRemoteObject.narrow(ref,CabinHomeRemote.class);
            CabinRemote cabin_1 = home.create(new Integer(1));
            cabin_1.setName("Master Suite");
            cabin_1.setDeckLevel(1);
            cabin_1.setShipId(1);
            cabin_1.setBedCount(3);
                
            Integer pk = new Integer(1);
            
            CabinRemote cabin_2 = home.findByPrimaryKey(pk);
            System.out.println(cabin_2.getName( ));
            System.out.println(cabin_2.getDeckLevel( ));
            System.out.println(cabin_2.getShipId( ));
            System.out.println(cabin_2.getBedCount( ));

        } catch (java.rmi.RemoteException re){re.printStackTrace( );}
          catch (javax.naming.NamingException ne){ne.printStackTrace( );}
          catch (javax.ejb.CreateException ce){ce.printStackTrace( );}
          catch (javax.ejb.FinderException fe){fe.printStackTrace( );}
    }

    public static Context getInitialContext( ) 
        throws javax.naming.NamingException {

        Properties p = new Properties( );
        // ... Specify the JNDI properties specific to the vendor.
        return new javax.naming.InitialContext(p);
    }
}

To access an enterprise bean, a client starts by using JNDI to obtain a directory connection to a bean's container. JNDI is an implementation-independent API for directory and naming systems. Every EJB vendor must provide a directory service that is JNDI-compliant. This means that they must provide a JNDI service provider, which is a piece of software analogous to a driver in JDBC. Different service providers connect to different directory services - not unlike JDBC, where different drivers connect to different relational databases. The getInitialContext( ) method uses JNDI to obtain a network connection to the EJB server.

The code used to obtain the JNDI Context depends on which EJB vendor you use. Consult your vendor's documentation to find out how to obtain a JNDI Context appropriate to your product. For example, the code used to obtain a JNDI Context in WebSphere might look something like the following:

public static Context getInitialContext( )
    throws javax.naming.NamingException {

    java.util.Properties properties = new java.util.Properties( );
    properties.put(javax.naming.Context.PROVIDER_URL, "iiop:///");
    properties.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY,
        "com.ibm.ejs.ns.jndi.CNInitialContextFactory");
    return new InitialContext(properties);
}

The same method developed for BEA's WebLogic Server would be different:

public static Context getInitialContext( )
    throws javax.naming.NamingException {
   
    Properties p = new Properties( );
    p.put(Context.INITIAL_CONTEXT_FACTORY, 
        "weblogic.jndi.WLInitialContextFactory");
    p.put(Context.PROVIDER_URL, "t3://localhost:7001");
    return new javax.naming.InitialContext(p);
}

Once a JNDI connection is established and a context is obtained from the getInitialContext( ) method, the context can be used to look up the EJB home of the Cabin EJB.

Object ref = jndiContext.lookup("CabinHomeRemote");

Throughout this book, we'll use lookup names like "CabinHomeRemote" for remote client applications. The actual name you use to do a lookup may be different, depending on the requirements of your vendor. You will need to bind a lookup name to the EJB server's naming service, and some vendors may require a special directory path.

If you are using a standard J2EE component (Servlet, JSP, EJB, or J2EE Application Client), you will not need to set the properties explicitly when creating a JNDI InitialContext, no matter which EJB vendor you are using. That's because the JNDI properties can be configured at deployment time and are applied automatically. A J2EE component would obtain its InitialContext as follows:

public static Context getInitialContext( )
    throws javax.naming.NamingException {
   
    return new javax.naming.InitialContext( );
}

This is simpler and more portable than configuring JNDI properties for simple Java clients. All J2EE components use the same JNDI naming system that enterprise beans use to lookup any service. Specifically, they require that EJB references be bound to the java:comp/env/ejb/ namespace. For example, for a J2EE component, here's all we need to look up the Cabin EJB:

Object ref = jndiContext.lookup("java:comp/env/ejb/CabinHomeRemote");

At deployment time you would use the vendor's deployment tools to map that JNDI name to the Cabin EJB's home. In this book, Java client applictions will need to use explicit parameters for JNDI lookups. As an alternative you could use a special J2EE component called a J2EE Application Client, but this type of component is outside the scope of this book. For more information about J2EE Application Client components consult the J2EE 1.3 (for EJB 2.0) or the J2EE 1.4 specifications.

The Client_1 application uses the PortableRemoteObject.narrow( ) method to narrow the Object ref to a CabinHomeRemote reference:

Object ref = jndiContext.lookup("CabinHomeRemote");
CabinHomeRemote home = (CabinHomeRemote)
    PortableRemoteObject.narrow(ref,CabinHomeRemote.class);

The PortableRemoteObject.narrow( ) method was first introduced in EJB 1.1 and continues to be used on remote clients in EJB 2.1 and 2.0. It is needed to support the requirements of RMI over IIOP. Because CORBA supports many different languages, casting is not native to CORBA (some languages don't have casting). Therefore, to get a remote reference to CabinHomeRemote, we must explicitly narrow the object returned from lookup( ). This has the same effect as casting and is explained in more detail in Chapter 5.

The name used to find the Cabin EJB's EJB home is set by the deployer using a deployment wizard like the one pictured earlier. The JNDI name is entirely up to the person deploying the bean; it can be the same as the bean name set in the XML deployment descriptor, or something completely different.

Pages: 1, 2, 3, 4

Next Pagearrow