From: <vh...@us...> - 2001-06-22 22:16:55
|
User: vharcq Date: 01/06/22 15:16:54 Modified: src/docs cmp.xml Log: Compliance 2.2 Revision Changes Path 1.10 +353 -361 manual/src/docs/cmp.xml Index: cmp.xml =================================================================== RCS file: /cvsroot/jboss/manual/src/docs/cmp.xml,v retrieving revision 1.9 retrieving revision 1.10 diff -u -r1.9 -r1.10 --- cmp.xml 2001/05/20 19:24:56 1.9 +++ cmp.xml 2001/06/22 22:16:54 1.10 @@ -7,23 +7,30 @@ </author> <email>k....@we...</email> </para> + <para>JBoss 2.2 compliance checked by:<author> + <firstname>Vincent</firstname> + <surname>Harcq</surname> + </author> + <email>vin...@hu...</email> + </para> + <section> <title>Introduction</title> <section> <title>What this article is about</title> - <para>This article presensts a step-by-step example of creating, deploying and + <para>This article presents a step-by-step example of creating, deploying and testing an Entity JavaBean that uses - `container-managed persistence' with the JBoss EJB server. This + "container-managed persistence" with the JBoss EJB server. This example is very simple, and is a variation of - the old favourite `music CD' case study. I have chosen this example + the old favourite "music CD" case study. I have chosen this example because the application is likely to be - familiar to most people without much explanation. In short, the `CD' + familiar to most people without much explanation. In short, the "CD" EJB models a music CD. Applications will want to add and delete CDs to a collection, and search the collection for specific CDs. There is also a - `CDCollection' EJB that lists and searches collections of CDs.</para> + "CDCollection" EJB that lists and searches collections of CDs.</para> <para> The full source code to accompany this chapter can be found in either zip (cdEJB.zip) or tar/gzip (cdEJB.tar.gz) format @@ -33,12 +40,6 @@ contains the Java source code, JavaDoc documentation, and a text file of test data (CDs.txt) to initialize the database.</para> - <para> -Although I will occasionally make reference to Linux, this is only because -that's what I use for EJB -development; most of the material in this article will work with any Unix -version, and even with Windows NT -if the directory structure is adjusted accordingly.</para> </section> <section> <title>Pre-requisites</title> @@ -54,33 +55,19 @@ might want to look at the chapter titled <quote>First Steps</quote> first.</para> </section> <section> - <title>JBoss hints</title> - <para>JBoss is written entirely in Java, as is the `Hypersonic' database with -which it is supplied. Searching a database -requires lots of repetitive operations, and an interpreting Java system will -be extremely slow if the database is -large. If you are using the Sun JDK, you will need to ensure that it is -configured to use the `Hotspot' virtual -machine, to achieve anything close to acceptable performance. For the purposes -of study, you could get along -by ensuring that the database size is kept small, but with even a hundred -objects you will find it too slow to use. -</para> - </section> - <section> <title>Persistence: review</title> <para>There are, in essence, two kinds of Enterprise JavaBean: session and entity. Entity EJBs contain information that is persistent across different client-Bean interactions, while session EJBs don't. For example, a class -called `Customer' that represents the customers of a particular service will +called "Customer" that represents the customers of a particular service will contain persistent information -(about the customer). A class called `CustomerFinder', say, that finds +(about the customer). A class called "CustomerFinder", say, that finds Customer instances that match certain criteria is likely to be a session EJB, because it does not require information that is maintained between different client-server interactions.</para> - <para> Session EJBs can be further divided into `stateless' and `stateful'. A + <para> Session EJBs can be further divided into "stateless" and "stateful". A stateful session EJB has a state that is persistent between invocations of its methods. A stateless EJB does not even have to retain its state between @@ -93,13 +80,11 @@ correspondence between instances of an entity EJB and rows of a database table. In practice, all EJB servers implement entity instances as table rows. This correspondence is so strong -that the notion of a `primary key' -is relevant to an entity EJB. Of course, a primary key is a database concept, -not an object-orientation concept -at all.</para> +that the database notion of a "primary key" +is relevant to an entity EJB. </para> <para> The persistence of an entity EJB may be managed by the Bean itself, or by the server (technically by the -`container'). The latter technique is called `Container-managed persistence', +"container"). The latter technique is called "Container-managed persistence", and is the subject of the rest of this article.</para> </section> @@ -114,7 +99,7 @@ accessing the database is left to the generated classes. </para> <para>The breed of CMP that has most value is the "real CMP" or a CMP where you let the container manage the -persistent representation of your beans entirerly. This might not work right +persistent representation of your beans entirely. This might not work right now for you if your object are complex but should work in most simple cases. EJB2.0 also goes the extra length to make the persistent @@ -129,10 +114,10 @@ <section> <title>Determine the persistent classes</title> <para>In this simple example we will use two Enterprise JavaBeans. The first, called -`CD' models a music CD. +"CD" models a music CD. It contains attributes (instance variables) that store the title ID code and various other -properties of a music CD. The second is called `CDCollection', and models a +properties of a music CD. The second is called "CDCollection", and models a collection of such CDs. This bean acts as a simple interface between the client and the CD Bean; technically we could @@ -176,10 +161,8 @@ the persistent attributes. </para> <para> In this example, I will assume that the persistent fields are all Java -`String' values representing -the ID, title, artist, type and notes. You could, of course, add other -information you feel to be -important. </para> +Primitive type values representing +the ID, title, artist, type and notes. </para> </section> </section> <section> @@ -190,214 +173,201 @@ present the full code for this Bean below; the code for the CDCollection Bean will not be discussed further because it -is not interesting in the context of container-managed persistence. Remember -that the full source code is available to download as an archive named<quote>cdEJB.zip</quote>. You can get it from the files area in the -documentation section on www.jboss.org.</para> - <para>CD.java: remote interface for the `CD' Bean</para> +is not interesting in the context of container-managed persistence. </para> + <para>CD.java: remote interface for the "CD" Bean</para> <para> - <programlisting>package com.web_tomorrow.cd; + <programlisting><![CDATA[ +package org.jboss.docs.cmp.cd.interfaces; +import javax.ejb.EJBObject; import java.rmi.RemoteException; -import javax.ejb.*; /** -This interface defines the remote interface to the `CD' EJB -*/ -public interface CD extends EJBObject + * This interface defines the remote interface for the CD Bean + */ +public interface CD +extends EJBObject { -/** -Get CD title -*/ -public String getTitle() throws RemoteException; + public Integer getId() throws RemoteException; -/** -Set CD title -*/ -public void setTitle(String title) throws RemoteException; + public void setId(Integer id) throws RemoteException; -/** -Set CD ID code -*/ -public String getId() throws RemoteException; + public String getTitle() throws RemoteException; -/** -Get CD ID code -*/ -public void setId(String id) throws RemoteException; + public void setTitle(String title) throws RemoteException; -/** -Get artist -*/ -public String getArtist() throws RemoteException; + public String getArtist() throws RemoteException; -/** -Set artist -*/ -public void setArtist(String artist) throws RemoteException; + public void setArtist(String artist) throws RemoteException; -/** -Get type (rock, classical, chamber music, etc) -*/ -public String getType() throws RemoteException; - -/** -Set type -*/ -public void setType(String type) throws RemoteException; + public String getType() throws RemoteException; -/** -Get notes (comments, not musical notes!) -*/ -public String getNotes() throws RemoteException; + public void setType(String type) throws RemoteException; -/** -Set notes -*/ -public void setNotes(String type) throws RemoteException; -}</programlisting> + public String getNotes() throws RemoteException; + + public void setNotes(String type) throws RemoteException; + +} +]]></programlisting> </para> <para> The remote interface specifies methods to get and set the attributes of the object. -That's all it needs to do in this example. Note that, as with any Java Bean, +That's all it needs to do in this example. Note that, as with any JavaBean, the -names of these methods must follow the standard convention; that is, if an +names of these methods follow the standard convention; that is, if an attribute -is called `String X' then the `get' and `set' methods must be defined as +is called "String X" then the "get" and "set" methods should be defined as follows: </para> <programlisting>String getX(); -void setX(String);</programlisting> - <para>CDHome.java: home interface for the `CD' Bean</para> - <programlisting>package com.web_tomorrow.cd; +void setX(String x);</programlisting> + <para>However you can declare any methods that return information on +the state of the entity bean. These methods must be implemented by the programmer +in the implementation class of the entity bean.</para> + <para>CDHome.java: home interface for the "CD" Bean</para> + <programlisting><![CDATA[ +package org.jboss.docs.cmp.cd.interfaces; +import javax.ejb.EJBHome; +import javax.ejb.CreateException; +import javax.ejb.FinderException; import java.rmi.RemoteException; -import javax.ejb.*; import java.util.Collection; /** -This interface defines the home interface for the `CD' EJB -*/ -public interface CDHome extends EJBHome + * This interface defines the home interface for the CD Bean + */ +public interface CDHome +extends EJBHome { -/** -Create a new CD instance -*/ -public CD create(String id) throws RemoteException, CreateException; - -/** -Find the CD with the specified ID. This method is not implemented by the Bean, -but by the container -*/ -public CD findByPrimaryKey (String id) throws RemoteException, -FinderException; -/** -Finds the CD whose `type' attribute matches that specified. This method is -implemented by the container -*/ -public Collection findByType (String type) throws RemoteException, -FinderException; + /** + * Create a new CD instance + */ + public CD create(Integer id) + throws RemoteException, CreateException; + + /** + * Find the CD with the specified ID. This method is implemented by the + * container + */ + public CD findByPrimaryKey (Integer id) + throws RemoteException, FinderException; + + /** + * Finds the CD whose "type" attribute matches that specified. + * This method is implemented by the container + */ + public Collection findByType (String type) + throws RemoteException, FinderException; + + /** + * Get all CD instances. This method is implemented by the container + */ + public Collection findAll() + throws RemoteException, FinderException; -/** -Get all CD instances. This method is -implemented by the container -*/ -public Collection findAll() throws RemoteException, FinderException; -}</programlisting> +} +]]></programlisting> <para> The important thing to note about this interface is that it specifies methods that don't need to be implemented. In this case, findByPrimaryKey(), findByType() and findAll() -are all examples of `finder' methods. The EJB specification requires that the +are all examples of "finder" methods. The EJB specification requires that the server be able to provide finder methods for all the persistent attributes in the object. So, for example, -if your class has an attribute `X', then the server will provide a `findByX' +if your class has an attribute "X", then the server will provide a "findByX" method to search -on that field. Note that with JBoss the search is `exact'; that is, it won't +on that field. Note that with JBoss the search is "exact"; that is, it won't accept wild-card characters or an incorrect mixture of upper- and lower-case letters. The findByPrimaryKey() searches on the primary key field; we will discuss how the primary key is specified later.</para> - <para>CDBean.java: implementation of the `CD' Bean</para> + <para>CDBean.java: implementation of the "CD" Bean</para> <para> - <programlisting>package com.web_tomorrow.cd; + <programlisting><![CDATA[ +package org.jboss.docs.cmp.cd.bean; +import javax.ejb.EntityBean; +import javax.ejb.EntityContext; import java.rmi.RemoteException; -import javax.ejb.*; /** -This class contains the implementation for the methods specified in the home -and remote interfaces for the `CD' EJB -*/ -public class CDBean implements EntityBean + * This class contains the implementation for the methods specified in the home + * and remote interfaces for the "CD" EJB + */ +public class CDBean +implements EntityBean { -transient private EntityContext ctx; + EntityContext ctx; -public String id; -public String title; -public String artist; -public String type; -public String notes; + public Integer id; + public String title; + public String artist; + public String type; + public String notes; + + /** + * Create an instance of a CD. Note that this method returns null because the real + * creation is managed by the EJB container. + */ + public Integer ejbCreate (Integer _id) + { + id = _id; + return null; + } + + /** + * Called when the object has been instantiated; does nothing in this example + */ + public void ejbPostCreate(Integer id) { } + + public String getTitle() { return title; } + public void setTitle(String _title) { title = _title; } + + public Integer getId() { return id; } + public void setId(Integer _id) { id = _id; } + + public String getArtist() { return artist; } + public void setArtist(String _artist) { artist = _artist; } + + public String getType() { return type; } + public void setType(String _type) { type = _type; } + + public String getNotes() { return notes; } + public void setNotes(String _notes) { notes = _notes; } + + public void setEntityContext(EntityContext ctx) { this.ctx = ctx; } + + public void unsetEntityContext() { ctx = null; } + + public void ejbActivate() { } + public void ejbPassivate() { } + public void ejbLoad() { } + public void ejbStore() { } + public void ejbRemove() { } -/** -Create an instance of a CD. Note that this method returns null because the real -creation is managed by the EJB container. -*/ -public String ejbCreate (String _id) -{ - id = _id; - return null; } - -/** -Called when the object has been instantiated; does nothing in this example -*/ -public void ejbPostCreate(String id) { } - -public String getTitle() { return title; } -public void setTitle(String _title) { title = _title; } - -public String getId() { return id; } -public void setId(String _id) { id = _id; } - -public String getArtist() { return artist; } -public void setArtist(String _artist) { artist = _artist; } - -public String getType() { return type; } -public void setType(String _type) { type = _type; } - -public String getNotes() { return notes; } -public void setNotes(String _notes) { notes = _notes; } - -public void setEntityContext(EntityContext ctx) { this.ctx = ctx; } - -public void unsetEntityContext() { ctx = null; } - -public void ejbActivate() { } -public void ejbPassivate() { } -public void ejbLoad() { } -public void ejbStore() { } -public void ejbRemove() { } -}</programlisting> +]]></programlisting> </para> <para> The CDBean class provides implementations of the methods that aren't provided automatically -by the EJB container. Note that the ejbCreate method returns `null', meaning +by the EJB container. Note that the ejbCreate method returns "null", meaning that the container should take care of initializing the instance in the server's process space. Because the CD Bean is essentially passive -- a data repository -- it only has -a few methods. -These classes (and the CDCollection classes) can be compiled in the usual way; -don't forget -to include the path to the JBoss EJB class library in your classpath, e.g.,</para> - <para> - <command>javac -classpath /usr/local/jboss/client/ejb.jar:. ....</command> - </para> +a few methods. </para> + <para>To be able to compile these classes, you need to add to your classpath +a jar containing definitions of javax.ejb.*. Usually this is done by ejb.jar or +j2ee.jar. With JBoss, you have to use instead jboss-client.jar which is a bundle of +all J2EE API (javax.ejb, javax.jms, javax.resource, javax.transaction). + JBoss, you</para> </section> <section> <title>Packaging and deploying the Beans</title> @@ -406,129 +376,143 @@ for a session bean. This information is provided in the deployment descriptor ejb-jar.xml</para> <programlisting><![CDATA[ -<enterprise-beans> - <entity> - <description>Models a music CD</description> - <ejb-name>CDBean</ejb-name> - <home>com.web_tomorrow.cd.CDHome</home> - <remote>com.web_tomorrow.cd.CD</remote> - <ejb-class>com.web_tomorrow.cd.CDBean</ejb-class> - <persistence-type>Container</persistence-type> - <prim-key-class>java.lang.String</prim-key-class> - <reentrant>False</reentrant> - <cmp-field><field-name>id</field-name></cmp-field> - <cmp-field><field-name>title</field-name></cmp-field> - <cmp-field><field-name>artist</field-name></cmp-field> - <cmp-field><field-name>type</field-name></cmp-field> - <cmp-field><field-name>notes</field-name></cmp-field> - <primkey-field>id</primkey-field> - </entity> +<?xml version="1.0"?> -<!-- more beans here --> +<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN" "http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd"> -</enterprise-beans> +<ejb-jar> + <display-name>MusicCDs</display-name> + <enterprise-beans> + + <entity> + <description>Models a music CD</description> + <ejb-name>CDBean</ejb-name> + <home>org.jboss.docs.cmp.cd.interfaces.CDHome</home> + <remote>org.jboss.docs.cmp.cd.interfaces.CD</remote> + <ejb-class>org.jboss.docs.cmp.cd.bean.CDBean</ejb-class> + <persistence-type>Container</persistence-type> + <prim-key-class>java.lang.Integer</prim-key-class> + <reentrant>False</reentrant> + <cmp-field><field-name>id</field-name></cmp-field> + <cmp-field><field-name>title</field-name></cmp-field> + <cmp-field><field-name>artist</field-name></cmp-field> + <cmp-field><field-name>type</field-name></cmp-field> + <cmp-field><field-name>notes</field-name></cmp-field> + <primkey-field>id</primkey-field> + </entity> + + <session> + <description>Models a music CD collection</description> + <ejb-name>CDCollectionBean</ejb-name> + <home>org.jboss.docs.cmp.cd.interfaces.CDCollectionHome</home> + <remote>org.jboss.docs.cmp.cd.interfaces.CDCollection</remote> + <ejb-class>org.jboss.docs.cmp.cd.bean.CDCollectionBean</ejb-class> + <session-type>Stateless</session-type> + <transaction-type>Container</transaction-type> + <ejb-ref> + <ejb-ref-name>ejb/CD</ejb-ref-name> + <ejb-ref-type>Entity</ejb-ref-type> + <home>org.jboss.docs.cmp.cd.interfaces.CDHome</home> + <remote>org.jboss.docs.cmp.cd.interfaces.CD</remote> + <ejb-link>CDBean</ejb-link> + </ejb-ref> + </session> + + </enterprise-beans> + + <assembly-descriptor> + <container-transaction> + <method> + <ejb-name>CDBean</ejb-name> + <method-name>*</method-name> + </method> + <trans-attribute>Required</trans-attribute> + </container-transaction> + </assembly-descriptor> +</ejb-jar> ]]></programlisting> - <para>The listing above shows the section of ejb-jar.xml that is relevant to the CD -Bean. It has the + <para>The first part of the listing above shows the section of ejb-jar.xml that +is relevant to the CD Bean. It has the usual information about the classes that consitute the Bean, but it also specifies the type and name of the primary key, and the fields that are persistent. Note that in this case the -`id' field gets listed twice: once as a persistent field and then again as the +"id" field gets listed twice: once as a persistent field and then again as the primary key field. It might be thought that specifying a field as a primary key would automatically make it persistent, -but it doesn't. Leaving out thecmp-field definition for the primary key -results in this error -message at deployment time:</para> - <para> - <computeroutput>[JAWS] Initializing JAWS plugin for CDBean -[Container factory] java.lang.NoSuchFieldException: CASE_INSENSITIVE_ORDER</computeroutput> - </para> - <para>The deployment descriptor for the CDCollection class does not require any +but it doesn't. </para> + <para>The second part of the listing is for the CDCollection session bean. +It does not require any persistence information, but it does require an ejb-ref section; this indicates that the CDCollection Bean refers to CD Bean instances. The ejb-ref section lists the type of the CD Bean, and all its classes. </para> - <programlisting><![CDATA[ - -<session> - <description>Models a music CD collection</description> - <ejb-name>CDCollectionBean</ejb-name> - <home>com.web_tomorrow.cd.CDCollectionHome</home> - <remote>com.web_tomorrow.cd.CDCollection</remote> - <ejb-class>com.web_tomorrow.cd.CDCollectionBean</ejb-class> - <session-type>Stateless</session-type> - <transaction-type>Container</transaction-type> - <ejb-ref> - <ejb-ref-name>ejb/CD</ejb-ref-name> - <ejb-ref-type>Entity</ejb-ref-type> - <home>com.web_tomorrow.cd.CDHome</home> - <remote>com.web_tomorrow.cd.CD</remote> - <ejb-link>com.web_tomorrow.cd.CDBean</ejb-link> - </ejb-ref> -</session> -]]></programlisting> - <para>In the JBoss run-time configuration file `jboss.xml' we should specify the + <para>In the JBoss run-time configuration file "jboss.xml" we should specify the JNDI names of the Beans, like this:</para> <programlisting><![CDATA[ -<entity> - <ejb-name>CDBean</ejb-name> - <jndi-name>cd/CD</jndi-name> -</entity> - -<session> - <ejb-name>CDCollectionBean</ejb-name> - <jndi-name>cd/CDCollection</jndi-name> -</session> +<?xml version="1.0" encoding="Cp1252"?> + +<jboss> + <secure>false</secure> + <container-configurations /> + <resource-managers /> + <enterprise-beans> + <session> + <ejb-name>CDCollectionBean</ejb-name> + <jndi-name>cd/CDCollection</jndi-name> + <configuration-name></configuration-name> + </session> + <entity> + <ejb-name>CDBean</ejb-name> + <jndi-name>cd/CD</jndi-name> + <configuration-name></configuration-name> + </entity> + </enterprise-beans> +</jboss> + ]]></programlisting> - <para>This says the `CDBean' has the JNDI name `cd/CD' and `CDCollectionBean' has -the JNDI name `cd/CDCollection'. -Note that the method of specifying these JNDI names depends on the server.</para> + <para>This says the "CDBean" has the JNDI name "cd/CD" and "CDCollectionBean" has +the JNDI name "cd/CDCollection". </para> <para>When packaging these Beans, don't forget to include the files ejb-jar.xml and jboss.jar in the directory META-INF.During deployment (simply copy the packaged beans to the -`deploy' subdirectory +"deploy" subdirectory of the JBoss directory) you should see a message like the following:</para> <literallayout> - <computeroutput>[Container factory] Deploying:file:/usr/lib/jboss/deploy/cd.jar + <computeroutput>[Default] JBoss 2.2.2 Started in 0m:5s +[Auto deploy] Auto deploy of file:/C:/Projects/jbosscvs22/jboss/dist/deploy/cd.jar +[J2EE Deployer Default] Deploy J2EE application: file:/C:/Projects/jbosscvs22/jboss/dist/deploy/cd.jar +[J2EE Deployer Default] Create application cd.jar +[J2EE Deployer Default] install module cd.jar +[Container factory] Deploying:file:/C:/Projects/jbosscvs22/jboss/dist/tmp/deploy/Default/cd.jar +[Verifier] Verifying file:/C:/Projects/jbosscvs22/jboss/dist/tmp/deploy/Default/cd.jar/ejb1001.jar [Container factory] Deploying CDBean [Container factory] Deploying CDCollectionBean -[JAWS] Initializing JAWS plugin for CDBean -[JAWS] Remove:DELETE FROM CDBean WHERE id=? -[JAWS] Drop:DROP TABLE CDBean -[JAWS] Create table:CREATE TABLE CDBean (notes VARCHAR(256),title -VARCHAR(256),artist VARCHAR(256),id VARCHAR(256),type VARCHAR(256)) -[JAWS] Insert:INSERT INTO CDBean (notes,title,artist,id,type) VALUES -(?,?,?,?,?) -[JAWS] Select:SELECT notes,title,artist,id,type FROM CDBean WHERE id=? -[JAWS] Table CDBean exists -[Container factory] Started: CDBean -[Container factory] Bind ejb/CD to com.web_tomorrow.cd.CDBean -[Container factory] Started: CDCollectionBean -[Container factory] Bound CDBean to cd/CD -[Container factory] Bound CDCollectionBean to cd/CDCollection -[Container factory] Deployed application: file:/usr/lib/jboss/deploy/cd.jar</computeroutput> +[JAWS] Created table 'CDBean' successfully. +[Bean Cache] Cache policy scheduler started +[Container factory] Deployed application: file:/C:/Projects/jbosscvs22/jboss/dist/tmp/deploy/Default/cd.jar +[J2EE Deployer Default] J2EE application: file:/C:/Projects/jbosscvs22/jboss/dist/deploy/cd.jar is deployed.</computeroutput> </literallayout> - <para>`JAWS' is the JBoss interface to the database engine. During deployment JAWS + <para>"JAWS" is the JBoss interface to the database engine. During deployment JAWS has -deleted any existing table called `CDBean', then created a new CDBean table +deleted any existing table called "CDBean", then created a new CDBean table with the specified column layout. How does it know to use VARCHAR(256) for each field? It doesn't: it's guessing because we haven't provided any other information. -During deployment, JAWS looks for a file called `jaws.xml'; if this file +During deployment, JAWS looks for a file called "jaws.xml"; if this file exists it is read to configure the names and geometry of the database tables. VARCHAR(256) is the default for String attributes. The default table name is the same as that of the Bean class, which is why we have ended up with a table called -`CDBean'. This also can be over-ridden in jaws.xml. In practice, the JAWS +"CDBean". This also can be over-ridden in jaws.xml. In practice, the JAWS defaults -are adequate for most applications. However, there may be speed advantages to +are adequate during developement. However, there may be speed advantages to using fixed-length fields (e.g., CHAR(XX) rather than variable-length ones if at all possible.</para> @@ -556,104 +540,112 @@ CDCollection bean as a mediator. CDCollection is a stateless session bean. In this example, the client -calls the `findAll' method to get references to all the CD objects currently +calls the "findAll" method to get references to all the CD objects currently in the system. To run this client, you will first need to get some CD objects created. You -can use the `Upload' client for this, to create CD instances from a text file.</para> +can use the "Upload" client for this, to create CD instances from a text file.</para> <para>To avoid the necessity to specify the URL of the Bean server in the client source code, this client reads the required information from a properties file called -`cd.properties'. +"cd.properties". The file should contain the URL and driver for the naming service, like this:</para> - <para><literallayout>java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory -java.naming.provider.url=localhost:1099</literallayout></para> + <para><literallayout>java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory +java.naming.provider.url=localhost +java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces</literallayout></para> <para>Of course, if your server and client are on different computers, you will need to -change `localhost' to the real location of the server.</para> - <para>Here is the full listing of the `List' client.</para> +change "localhost" to the real location of the server. The JNDI protocol prefix used by JBoss is "jnp:". +The URL can also take these values:</para> +<para><literallayout>localhost +localhost:1099 +jnp://localhost:1099</literallayout></para> + <para>Here is the full listing of the "List" client.</para> <programlisting><![CDATA[ +package org.jboss.docs.cmp.cd; -package com.web_tomorrow.cd; -import javax.naming.*; +import org.jboss.docs.cmp.cd.interfaces.CD; +import org.jboss.docs.cmp.cd.interfaces.CDHome; +import org.jboss.docs.cmp.cd.interfaces.CDCollectionHome; +import org.jboss.docs.cmp.cd.interfaces.CDCollection; +import javax.rmi.PortableRemoteObject; +import javax.naming.InitialContext; import java.util.Hashtable; -import javax.rmi.PortableRemoteObject; import java.util.Properties; import java.io.FileInputStream; /** -This is a simple client for the `CD' EJB; it lists (to standard output) all -the `CD' instances in -the system. The `main' method allows this class to be run from the command -line. -*/ -class List -{ -public static void main(String[] args) + * This is a simple client for the "CD" EJB; it lists (to standard output) all + * the "CD" instances in the system. The "main" method allows this class to be + * run from the command line. + */ +public class List { - // Get information about the Bean server from the properties file - Properties props = new Properties(); - Properties sysProps = System.getProperties(); - try - { - props.load (new FileInputStream ("cd.properties")); - sysProps.putAll(props); - } - catch (Exception e) - { - System.err.println ("Can't read `cd.proprties'"); - System.exit (-1); - } - System.setProperties (sysProps); - - // Enclosing the whole process in a single `try' block is not an ideal way - // to do exception handling, but I don't want to clutter the program up - // with catch blocks - try - { - // Get a naming context - InitialContext jndiContext = new InitialContext(); - - // Get a reference to a CD Bean - Object ref = jndiContext.lookup("cd/CDCollection"); - - // Get a reference from this to the Bean's Home interface - CDCollectionHome home = (CDCollectionHome) - PortableRemoteObject.narrow (ref, CDCollectionHome.class); - - CDCollection cdCollection = home.create(); - - CD[] cds = cdCollection.findAll(); - for (int i = 0; i < cds.length; i++) - { - System.out.println (cds[i].getId() + "\t" + cds[i].getTitle() + "\t" + - cds[i].getArtist() + "\t" + cds[i].getType()); - } - } - catch(Exception e) - { - System.out.println(e.toString()); - } - } + public static void main(String[] args) + { + // Get information about the Bean server from the properties file + Properties props = new Properties(); + Properties sysProps = System.getProperties(); + try + { + props.load (new FileInputStream ("cd.properties")); + sysProps.putAll(props); + } + catch (Exception e) + { + System.err.println ("Can't read cd.properties"); + System.exit (-1); + } + System.setProperties (sysProps); + + // Enclosing the whole process in a single "try" block is not an ideal way + // to do exception handling, but I don't want to clutter the program up + // with catch blocks + try + { + // Get a naming context + InitialContext jndiContext = new InitialContext(); + + // Get a reference to a CD Bean + Object ref = jndiContext.lookup("cd/CDCollection"); + + // Get a reference from this to the Bean's Home interface + CDCollectionHome home = (CDCollectionHome) + PortableRemoteObject.narrow (ref, CDCollectionHome.class); + + CDCollection cdCollection = home.create(); + + CD[] cds = cdCollection.findAll(); + for (int i = 0; i < cds.length; i++) + { + System.out.println (cds[i].getId() + "\t" + cds[i].getTitle() + "\t" + + cds[i].getArtist() + "\t" + cds[i].getType()); + } + } + catch(Exception e) + { + System.out.println(e.toString()); + } + } + } ]]></programlisting> <para>To run this client you will need to specify in the CLASSPATH the location the -JBoss client libraries, so the command line will be at least like this:</para> - <para> - <command>java -classpath -$CLASSPATH:/usr/lib/jboss/client/jboss-client.jar:/usr/lib/jboss/client/jbosssx-client.jar:/usr/lib/jboss/client/jnp-client.jar com.web_tomorrow.cd.List;</command> - </para> - <para>If all is well, you will get a list of CDs.So we've created an entity EJB, and -a client -program that uses it.You'll agree, I hope, that it isn't that much more -complicated than +JBoss client libraries. These are jboss-client.jar, jbosssw-client.jar and jnp-client.jar.</para> + <para>You'll agree, that it isn't that much more complicated than creating a session EJB. The additional steps required are: </para> <para>The ejb-jar.xml file needs to indicate that the object is persistent, and list the persistent fields. It also needs to specify the name and class of the primary key field. </para> <para>If the default column names and types aren't what you need, create a file jaws.xml to specify them.</para> + <para>You should know have a lot of questions : Which database JBoss used to store the +data and how can I specify another one? How can I change the definition of the table that JBoss +created? Which control does I have on finder methods?</para> + <para>All these questions will be answered in the next chapter that will introduce the +specific CMP deployment descriptor "jaws.xml" and its default "standardjaws.xml". Note that this +default has has been used under the cover by JBoss for this example.</para> </section> <section> <title>Discussion: container-managed persistence</title> @@ -669,8 +661,8 @@ </para> <section> <title>Technical overview </title> - <para>In the EJB field there is a very strong correspondence between `rows of a -database table', and `instances of an object'. It is clear that the EJB + <para>In the EJB field there is a very strong correspondence between "rows of a +database table", and "instances of an object". It is clear that the EJB developers had this notion in mind from the very beginning. While the specification doesn't stipulate that persistence is provided by database tables, in practice it always is. Moreover, it is tacitly assumed that the @@ -698,18 +690,18 @@ generate database access statements with the efficiency of a human programmer. Consider this example: suppose I have an database table containing details of my music CD collection. I want to search ithe -collection for any one which has the text `Chopin' (case insensitive) in -either the `title' or `notes' column. In SQL I could write a statement like +collection for any one which has the text "Chopin" (case insensitive) in +either the "title" or "notes" column. In SQL I could write a statement like this: <programlisting> SELECT FROM CD WHERE title LIKE "%chopin%" OR notes LIKE "%chopin%"; </programlisting> </para> <para>The % character is an SQL wild-card and takes care of finding the required -string somewhere inside the field; the `LIKE' operator is +string somewhere inside the field; the "LIKE" operator is case-insensitive by default. How could we achieve this with a -container-managed EJB? If `CD' is an EJB, the container-supplied method -`findAll()' in its home interface will get all the current instances of `CD'. +container-managed EJB? If "CD" is an EJB, the container-supplied method +"findAll()" in its home interface will get all the current instances of "CD". In practice it will do this by executing a statement like @@ -721,7 +713,7 @@ store the primary key from each row of the database into the appropriate attribute of each CD instance. Then the program must examine the objects one at a time, checking whether they meet the required -criteria (i.e., the word `Chopin' in the appropriate attributes). As the +criteria (i.e., the word "Chopin" in the appropriate attributes). As the program iterates throuugh the objects, the server must cause their attributes to be read from the table; it won't have done this until now because it would try to conserve memory. So for each object examined @@ -743,8 +735,8 @@ <programlisting> UPDATE CD SET artist="Bloggs" WHERE ID="200"; </programlisting> </para> - <para>for example. This is efficient, but requires the that `Artist' field really be -called `artist'. This makes it difficult to change the names of columns in + <para>for example. This is efficient, but requires the that "Artist" field really be +called "artist". This makes it difficult to change the names of columns in the table. Alternatively the server could do a SELECT to get the current column values, delete the whole row, then insert a row with modified values. This allows a number of values to change at once and, because all @@ -757,7 +749,7 @@ <title>Limitations of late initialization </title> <para>Suppose we want to find whether a CD with a specific ID exists on the system. With CMP this corresponds to finding whether there is a row in -the database table with the corresponding value of the `id' column. The code +the database table with the corresponding value of the "id" column. The code in Java might look like this:<programlisting>// Get a reference to a CD Bean Object ref = jndiContext.lookup("cd/CD"); @@ -768,8 +760,8 @@ // Find the matching CD CD cd = home.findByPrimaryKey("xxx");</programlisting> </para> - <para>What will happen if `XXX' is not the ID of a CD that exists? There would seem -to be two sensible approaches. Either `findByPrimaryKey' + <para>What will happen if "XXX" is not the ID of a CD that exists? There would seem +to be two sensible approaches. Either "findByPrimaryKey" could throw an exception, or perhaps it could return a null reference. In either case the client could easily tell whether the object exists. In practice, the EJB server may do neither of these things. It may well return a @@ -778,7 +770,7 @@ initialization won't happen until the object is really required. This is done to improve efficiency; there is, after all, no need to initialize the object unless it will be needed. However, if the program continues to execute on -the basis that `cd' refers to a valid object, an exception will be thrown +the basis that "cd" refers to a valid object, an exception will be thrown later when the program tries to interact with it. This may not be a problem; if the ID had been generated from some earlier database access then we may be sure it really exists, and any failure to find it in the @@ -788,7 +780,7 @@ an object after getting a reference to it, like this:<programlisting>CD cd = home.findByPrimaryKey("xxx"); String dummy = cd.getId();</programlisting> </para> - <para>If there is no CD whose ID field is `XXX' then this will throw a + <para>If there is no CD whose ID field is "XXX" then this will throw a java.rmi.NoSuchObjectException. This gets around the problem of late initialization, but at the cost of an additional SQL access.</para> </section> @@ -796,17 +788,17 @@ <title>Suitability of container-managed persistence </title> <para>In many applications of object-oriented programming we have had to accept that some things that are philosophically objects are in reality -implemented as something else. The `something else' may be a row of a database +implemented as something else. The "something else" may be a row of a database table, or a line of a text file, or whatever; at some point we had to code the interface between the object-oriented system and the -`something elses'. Entity JavaBeans goes some way towards +"something elses". Entity JavaBeans goes some way towards eliminating this problem; things that are philosophically object can be modelled as objects, with methods and persistence. But this comes at a -cost. It's worth asking whether the `CD' EJB in the tutorial example really is +cost. It's worth asking whether the "CD" EJB in the tutorial example really is an object in a meaningful sense. It has attributes, but it doesn't do very much. We don't really gain all that much by making it an object; it could have remained a database row, and been manipulated through the -`CDCollection' class. Of course this isn't as elegant, but elegance can come +"CDCollection" class. Of course this isn't as elegant, but elegance can come at a high price. </para> <para>In summary then, container-managed persistence is straightforward to implement using JBoss (or any other EJB server, for that matter) |