EJB – Enterprise Java Beans

 

This chapter focuses on how to create the smallest EJB bean and call it from a JavaServlet. This chapter is a little complex as there are a lot of things we have to do and hence please follow every step with surgical precision. The guys at sun have given a batch file called asadmin that will be used extensively to register whatever our objects with the J2EE server or application server. This batch file is present in the c:\Sun\appserver\bin directory.

 

Asadmin.bat

call C:\Sun\AppServer\config\asenv.bat

set Path=%AS_INSTALL%\bin;%AS_ICU_LIB%;%PATH%

"%AS_JAVA%\bin\java" -Dcom.sun.aas.instanceRoot=%AS_INSTALL% -Dserver.name=server -Djava.library.path="%AS_INSTALL%\bin";"%AS_ICU_LIB%" -Dcom.sun.aas.configRoot="%AS_CONFIG%" -Djava.endorsed.dirs="%AS_INSTALL%\lib\endorsed" -cp "%AS_INSTALL%\lib";"%AS_INSTALL%\lib\appserv-rt.jar";"%AS_INSTALL%\lib\appserv-ext.jar";"%AS_INSTALL%\lib\j2ee.jar";"%AS_INSTALL%\lib\admin-cli.jar";"%AS_INSTALL%\lib\appserv-admin.jar";"%AS_INSTALL%\lib\commons-launcher.jar";"%AS_ANT_LIB%\ant.jar";"%AS_ANT_LIB%\optional.jar";"%AS_IMQ_LIB%\imqadmin.jar" com.sun.enterprise.cli.framework.CLIMain  %*

 

asenv.bat

set AS_ANT=C:\Sun\AppServer\lib\ant

set AS_ANT_LIB=C:\Sun\AppServer\lib\ant\lib

set AS_POINTBASE=C:\Sun\AppServer\pointbase

set AS_WEBSERVICES_LIB=C:\Sun\AppServer\lib

set AS_PERL=C:\Sun\AppServer\lib\perl

set AS_NSS=C:\Sun\AppServer\bin

set AS_NSS_BIN=C:\Sun\AppServer\lib\admincgi

set AS_IMQ_LIB=C:\Sun\AppServer\imq\lib

set AS_IMQ_BIN=C:\Sun\AppServer\imq\bin

set AS_CONFIG=C:\Sun\AppServer\config

set AS_INSTALL=C:\Sun\AppServer

set AS_JAVA=C:\Sun\AppServer\jdk

set AS_ACC_CONFIG=C:\Sun\AppServer\domains\domain1\config\sun-acc.xml

set AS_JHELP=C:\Sun\AppServer\lib

set AS_ICU_LIB=C:\Sun\AppServer\bin

set AS_LOCALE=en_US

set AS_DEF_DOMAINS_PATH=C:\Sun\AppServer\domains

 

The batch file asadmin.bat does two things. One it calls the asenv batch file that simply sets up the environment for the next command that calls the java program and specifies the class CLIMain. This class is the one that registers all the objects with the appserver.

 

If you use windows like we do, you will first need to start the Appserver or start the default domain and there is a menu option that does it for us.

 

Start-Programs- Sun Microsystems- J2ee 1.4 SDK – Start Default Server

 

This calls another batch file, asadmin-pause  as

 

C:\Sun\AppServer\lib\asadmin-pause.bat start-domain   domain1

 

Asadmin-pause.bat

call "C:\Sun\AppServer\config\asenv.bat"

call "%AS_INSTALL%\bin\asadmin.bat" %*

pause

 

Someone sleeping at sun as the asenv.bat file is being called twice. The asadmin-pause bat file simply calls the asadmin.bat file. The sun J2EE server has a concept of multiple domains, which we will explain later. We need to tell the application server the name of the domain it needs to work in or place all our applications.

 

When we install the appserver, we have one domain domain1 created for us. If you are like us and would like to start from scratch, we will show you how to create a domain. But please, please understand that all the applications and other objects registered with the domain will be lost. We would first like you to start the default domain from the menu.

 

Starting a appserver takes time and the first message tells us that the log file server.log present in C:\sun\appserver\domains\domain1\logs directory will contain all activity taking place. When the app server starts, pressing any key at the pause will close the initial window. We would now like to stop the app server.

 

asadmin stop-domain domain1

 

We use the argument stop-domain and the name of the domain domain1 as the second parameter. This stops the domain domain1 after some time. Then we run the next command that actually deletes the domain.

 

asadmin delete-domain domain1

 

It is this command that actually removes the directory domain1 under domain.

 

Finally lets create a new domain called domain1.

 

asadmin create-domain --adminport 4848  domain1

 

The only added option we have to specify is the port that we will use to connect whenever we would like to administer the domain. The default HTTP port is 80, we will use 8080 for our http connections and for administration we will use port 4848 because this is what sun is comfortable with.

 

Now that we have created on our domain domain1 from scratch, lets us now write our smallest application. But first start the default domain yourselves or from the menu option.

 

We will first create a sub-directory ejb3 and within this create another subdirectory src. We will place all our files in this directory src. This is not necessary but all the coders who have coded in J2EE, place the code in the src directory. Being superstitious so will we. Then all the files that will be generated by the system, will be stored in the build directory from ejb3.

 

Not necessary again. We would like to see all our files in one directory, but the J2EE world believes that unless you do not spread your files, over a zillion directories, a million levels deep, they are not happy.

 

There are about 11 files to be created in the src subdirectory.

 

 

Build.xml

<project default="vijay" >

<target name="vijay">

<delete dir="../build" />

<mkdir dir="../build" />

<javac srcdir="." destdir="../build" classpath="C:\Sun\AppServer\lib\j2ee.jar" />

<jar destfile="../build/VijayWarContainsServlet.war" update="true">

<zipfileset dir="." includes="starthtmlfile.html,VijayJsp.jsp"/>

<zipfileset dir="../build" includes="VijayServletClass.class" prefix="WEB-INF/classes/"/>

<zipfileset dir="." includes="sun-web.xml,web.xml"  prefix="WEB-INF"/>

</jar>

<jar destfile="../build/VijayJarContainsEJB.jar">

<zipfileset dir="../build/" includes="VijayRemoteInterface.class,VijayEJBClass.class,VijayHomeInterface.class" />

<metainf dir="." includes="ejb-jar.xml,sun-ejb-jar.xml"/>

</jar>

<jar destfile="../build/VijayFinalEar.ear"  update="true">

<metainf dir="." includes="application.xml" />

<zipfileset dir="../build" includes="VijayJarContainsEJB.jar,VijayWarContainsServlet.war" />

</jar>

<exec executable="cmd.exe" >

<arg line=" /c C:/Sun/AppServer/bin/asadmin deploy --user admin --password vijay712 --host office --port 4848 --name VijayAppName  ../build/VijayFinalEar.ear" />

</exec>  

</target>

<target name="vijay1">

<exec executable="cmd.exe" >

<arg line=" /c C:/Sun/AppServer/bin/asadmin undeploy --user admin --password vijay712 --host office --port 4848 VijayAppName" />

</exec>  

</target>

</project>

 

Ant is the default build tool used and hence the first file we create is build.xml. We start with the project tag and specify the default project as vijay. In this target we first delete the directory c:\ejb3\build using the delete dir task as this contains all files generated by the J2EE tools.

 

This directory is then recreated using the task mkdir. Once the directory is created, using the java compiler javac, the java files in the current directory, src, are compiled with the jar file in the classpath. However the compiled class files are placed in the build directory.  There are four java files in the src directory which get compiled.  As mentioned earlier the ant tasks like to be presented with directories where they performs their work. Thus the four class files will be created in c:\ejb3\build.  The java files are given below in the chapter.

 

The jar task builds our first war or web application archive file. We could have used the war file, but chose to use the jar task as a jar or war or ear files are basically the same but used for different purposes. They are all zip archives. The file called VijayWarContainsServlet.war will be created in the build directory.

 

An entity that begins with Vijay is created. The update attribute is set to true though not required. All that it does is if the destfile already exists, the files are added to the existing file thus not creating a new destfile. The zipfileset attribute details the files that will be added to the ear file. The dir attribute specifies a directory from which all files present will be added to the archive.

 

However we may want only some files from the current directory to be added to the first war file.

Thus using the includes attribute, individually the file names are specified or we could also give wildcards. Thus the first zipfileset task adds two files starthtmlfile.html and VijayJsp.jsp to the archive. Then the second zipfileset takes one file VijayServletClass.class  from the build directory and adds it to the archive.

 

This means that the first java program is called VijayServletClass.java. This class file will go into the WEB-INF/classes directory within the archive as the prefix attribute is given this value. Finally the WEB-INF directory within the archive gets the files web-xml and sun-web.xml.

 

The J2EE world uses xml files to specify what is present in the archive and and what files are for deployment. This is a major use of xml. But before we understand the java files, we first take a look at what the two xml files contain.

 

 

web.xml

<web-app>

<servlet>

<servlet-name>VijayServletName</servlet-name>

<servlet-class>VijayServletClass</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name> VijayServletName </servlet-name>

<url-pattern>/VijayServletUrl</url-pattern>

</servlet-mapping>

<ejb-ref>

<ejb-ref-name>ejb/VijayJNDIName</ejb-ref-name>

<ejb-ref-type>Session</ejb-ref-type>

<home>VijayHomeInterface</home>

<remote>VijayRemoteInterface</remote>

<ejb-link>VijayEJBName</ejb-link>

</ejb-ref>

<welcome-file-list>

<welcome-file>starthtmlfile.html</welcome-file>

</welcome-file-list>

</web-app>

 

We then briefly touched this deployment Descriptor before. It starts with the root element web-app. Then moving to the bottom is the welcome-file-list element that tells us that if we do not specify a file name, the one sent across will be starthtmlfile.html. The question is how do we activate this file.

 

http://localhost:8080/VijayContextRoot/

 

We write the above Url in the browser keeping in mind that the J2EE server runs on the same machine and the default port is 8080 and not 80. We would like to access our application by using the name VijayContextRoot. This name must be defined someplace in a xml file. This file will be dealt with a little later. 

 

 

Starthtmlfile.html

<html>

<form method="POST" action="/VijayContextRoot/VijayServletUrl">

<input type="text" name="name">

<p>

<input type="submit" value="Process">

</form>

</html>

 

This is a very simple file. All that it does is has a text box called name and a submit button with a label of Process. When we write some text in the text box and then click on the button, the Url written in the browser will be

 

http://localhost:8080/VijayContextRoot/VijayServletUrl

 

Thus the J2EE server will now have to resolve the name VijayServletUrl as the name VijayContextRoot is the name or context root of our application. 

 

The words VijayServletUrl identify a servlet and is called a url-pattern. If we look at the web.xml file, we will come across an element servlet-mapping. This contains an element url-element which defines the word VijayServletUrl. This tells the server that whenever a url is of Url pattern, execute a servlet with a name VijayServletName as it is the value of element servlet-name.

 

The only problem is the class that contains the code of the servlet called VijayServletName. All that we need to do is go a little up in the same file web.xml till we come across the servlet element  that defines all servlets in the war file. Thus the servlet-name tag gives us the name of the servlet and the servlet-class tag the servlet name which is VijayServletClass.

 

The .class extension is not necessary. This is how the Url VijayServletUrl gets mapped to the servlet file VijayServletClass. This is what the xml files are good for. They contain all the glue that binds everything together. The tag ejb-ref will be done later as this deals with Enterprise Java Beans or EJB’s.

 

Coming back to the other xml file, sun-web.xml, this file is present in the includes, but as we have not created it, we get no errors at all. Thus we have created one war file already and if we look at the directory structure using the WinZip program , this is what we will see.

 

We  have a total of five files in the archive. We had only four files. The extra file is called a manifest file Manifest.mf in the meta-inf directory. Its contents are 

 

Manifest.mf

Manifest-Version: 1.0

Ant-Version: Apache Ant 1.5.3

Created-By: 1.4.2_02-b03 (Sun Microsystems Inc.)

 

Nothing useful. The files VijayJsp.jsp and starthtmlfile.html are in the root. The file VijayServletClass.class file is in the WEB-INF/classes and the web.xml file in the web-inf directory. Thus this war file basically contains everything but our EJB which is being packaged in the next file.

 

Now lets proceed to the second archive which contains the EJB. This archive is called VijayJarContainsEJB.jar. We keep all our servlets and jsp’s and html files in a war file as they gets executed by the Tomcat server which is part of the Apache group. The EJB are executed by the sun application server.

 

This is why by convention, EJB’s are in a jar file and servlets in a war file. Both are zip archives. Every EJB is made up of two interfaces and one class file. In our case the interface files are VijayRemoteInterface and VijayHomeInterface and the class is VijayEJBClass. The deployment descriptors  are two xml files, ejb-jar.xml and sun-ejb-jar.xml. These files are placed in the meta-inf directory using the task metainf.

 

 

 

ejb-jar.xml

<ejb-jar>

<enterprise-beans>

<session>

<display-name>VijayEJBName</display-name>

<home>VijayHomeInterface</home>

<remote>VijayRemoteInterface</remote>

<ejb-class>VijayEJBClass</ejb-class>

</session>

</enterprise-beans>

</ejb-jar>

 

The file ejb-jar.xml starts with the root tag of ejb-jar followed by the tag enterprise-beans and session. Then the tag display-name tells us that the name of the EJB is VijayEJBName. This is the name that we will give to our EJB and refer to it. Every EJB has two interfaces which are called the home and remote interface. The home interface definition is as follows.

 

 

VijayHomeInterface.java

public interface VijayHomeInterface extends javax.ejb.EJBHome

{

public VijayRemoteInterface create() throws java.rmi.RemoteException, javax.ejb.CreateException;

}

 

The interface VijayHomeInterface extends the EJBHome class and has only one function called create. Most java functions throw exceptions and these are part of the method signature and cannot be avoided. The function create is used to create the remote interface VijayRemoteInterface.

 

 

VijayRemoteInterface.java

public interface VijayRemoteInterface extends javax.ejb.EJBObject

{

public String ourEJBfunction() throws java.rmi.RemoteException;

}

 

The remote interface is derived from EJBObject and contains a list of all functions that comprise our bean. We would like our bean to have only one function ourEJBfunction that will return a string and throw a exception if an error occurs.

 

The actual code of our EJB will be in the third java file.

 

 

VijayEJBClass.java

public class VijayEJBClass implements javax.ejb.SessionBean

{

public void setSessionContext (javax.ejb.SessionContext ctx)

{

System.out.println("setSessionContext");

}

public void ejbRemove()

{

System.out.println("ejbRemove");

}

public void ejbPassivate()

{

System.out.println("ejbPassivate");

}

public void ejbActivate()

{

System.out.println("ejbActivate");

}

public void ejbCreate() throws java.rmi.RemoteException, javax.ejb.CreateException

{

System.out.println("ejbCreate");

}

public String ourEJBfunction() throws java.rmi.RemoteException

{

System.out.println("ourEJBfunction");

return " EJB16 ";

}

}

 

This class does not extend a class but implements  SessionBean. Thus we have  no choice but to bring in the five functions setSessionContext, ejbRemove, ejbPassivate, ejbActivate and ejbCreate.

 

We will explain the use of these later. The above five functions are a must and if we do not want to get a runtime error we need to also implement the method ourEJBfunction. This method will be called from the servlet and simply return a string. We have placed the println functions in every method as we would want to know when the system calls the above functions. These println’s will show in the log file server.log.

 

Coming back to the ejb-jar file, we use the three tags home, remote and ejb-class to specify the names of the three EJB classes and interfaces. Now lets look at the file sun-ejb-jar.xml

 

 

sun-ejb-jar.xml

<sun-ejb-jar>

<enterprise-beans>

<ejb>

<ejb-name>VijayEJBName</ejb-name>

<jndi-name>VijayJNDIName</jndi-name>

</ejb>

</enterprise-beans>

</sun-ejb-jar>

 

This file has the root element sun-ejb-jar followed by the tags enterprise-beans and ejb. This file is used to map the name of the EJB VijayEJBName denoted by the element ejb-name and the JNDI name VijayJNDIName.

 

The Java Naming and Directory Interface is a way of naming something so that others can find it. It is one of the pillars of the java world. Thus in our servlet  we will refer to the JNDIName VijayJNDIName and get the name of the EJB and from there the classes that represent the bean.

 

In the file web.xml, we use the syntax ejb/VijayJNDIName as the reference to the bean and once again stated the three classes/interfaces that represent our bean. Also the type of the bean is Session as there are many bean types. Looking into the jar file in WinZip we see the following files.

 

Six files with the meta-inf directory containing the system created Manifest.mf file and the two xml files ejb-jar.xml and sun-ejb-jar.xml. The other three class files are present in the root.

 

In the web.xml file we use the element ejb-ref to connect the JNDI name VijayJNDIName to the type of bean Session and the three classes/interfaces that represent the bean. We are duplication this information twice.

 

Finally in build.xml we have created the third file, VijayFinalEar.ear. An EAR file is a enterprise application resource file and contains all the archives created so far. Thus we first include the jar and war file and in the meta-inf  directory the first xml file to be read application.xml.

 

 

application.xml

<application>

<module>

<ejb>VijayJarContainsEJB.jar</ejb>

</module>

<module>

<web>

<web-uri>VijayWarContainsServlet.war</web-uri>

<context-root>VijayContextRoot</context-root>

</web>

</module>

</application>

 

The ear file is the final file that packages our application. The file application.xml tells the App server the contents of this file. A EAR file is made up of modules and our ear file has two modules and we have thus two module tags.

 

The first specifies VijayJarContainsEJB.jar as the first module file with no tags describing its contents as it only contains EJB’s. The second module is the one that contains the servlets and jsp’s and its name is denoted by the tag  VijayWarContainsServlet.war.

 

The  tag context-root is the one that specifies the name of the application to be used in the Url, VijayContextRoot. Now when we run the ant program it creates the files advertised and we must register this ear file with the system. The task exec is what we use for this.

 

This task lets us execute any executable we want and we specify the command prompt cmd.exe as the value of the executable attribute. Then we specify the arguments to using the attribute line and task arg. We can not specify a batch file as the name of the executable as the CreateProcess command used gives us an error.

 

Basically all that we are doing is running the asadmin batch file specifying the option deploy to deploy the file VijayFinalEar.ear specified at the end. For the deploy option we need to specify the user name admin, the password vijay712 that we specified at the time of installation.  

 

Then the name of the host office and the admin port number 4848.  The name of the application VijayAppName  if not specified defaults to the name of the ear file. This command could also be run by us and the application would get deployed. When we install or deploy the application, the words uri VijayContextRoot denote the application. When we write the url  http://localhost:8080/VijayContextRoot/ in the browser, the system looks up the context root and displays the initial file specified in the web.xml file. Then when we click on the button it invokes the servlet as explained earlier. Lets now look at the servlet code.

 

 

 

VijayServletClass.java

import javax.servlet.*;

import java.io.*;

import javax.servlet.http.*;

import javax.naming.*;

import javax.rmi.PortableRemoteObject;

public class VijayServletClass extends HttpServlet

{

public void doPost (HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException

{

System.out.println("doPost");

VijayHomeInterface m_VijayHomeInterface;

VijayRemoteInterface m_VijayRemoteInterface;

InitialContext m_InitialContext;

try

{

m_InitialContext = new javax.naming.InitialContext();

Object m_Object = m_InitialContext.lookup("java:comp/env/ejb/VijayJNDIName");

m_VijayHomeInterface=(VijayHomeInterface)PortableRemoteObject.narrow(m_Object,VijayHomeInterface.class);

m_VijayRemoteInterface = m_VijayHomeInterface.create(); 

}

catch(Exception e) {return;} 

String s = m_VijayRemoteInterface.ourEJBfunction() + " servlet16 ";

request.setAttribute("ourattribute", s);

response.setContentType("text/html");

ServletContext m_ServletContext = getServletContext();

RequestDispatcher m_RequestDispatcher;

m_RequestDispatcher = m_ServletContext.getRequestDispatcher("/VijayJsp.jsp");

m_RequestDispatcher.include(request, response);

return; 

} 

}

 

A servlet in the java world drives from the class HttpServlet. As the method is post in the file starthtmlfile.html, the function doPost gets called. This functions signature specifies the exceptions it throws. We need a handle to our EJB object and all that we have is the name VijayJNDIName from the xml files.

 

Thus we create a object m_InitialContext of  type class InitialContext that comes from the JNDI namespace using the new keyword as always. It is the class InitialContext that is the class used for performing naming lookups.  We first use the lookup method passing it the JNDI name of the object we want. This function requires the JNDI that follows a certain format.

 

The java:comp/env is reserved. In the xml file we specified the JNDI name as ejb/VijayJNDIName. The lookup function locates the object for us and returns a reference to us. In our case it is our EJB. Java uses RMI or Remote Method Invocation for distributed computing. The class PortableRemoteObject comes from the javax.rmi namespace.

 

This class has a static method narrow that simply does an extra error check. The Object m_Object that we retrieved using the lookup method is as mentioned before a reference to our EJB. Our EJB actually is made up of two interfaces and a class. The interface that represents our EJB is aptly called VijayHomeInterface.

 

What we need is a extra error check that the Object m_Object can be casted into a VijayHomeInterface object. For this we use the narrow method passing it the object that we want to check the type conversion and the class we will cast to VijayHomeInterface. As narrow returns a Object a lower type , we need the cast.

 

We could have done without the narrow method and directly casted m_VijayHomeInterface to m_Object as m_VijayHomeInterface=(VijayHomeInterface)m_Object but as the J2EE sample uses narrow, we decided so shall we. Thus the first interface that we get for a EJB is the home interface.

 

This is why it is called the home interface. It also has only one method create. This method is what we use to give us the Remote Interface m-VijayRemoteInterface. If you remember it is in the Remote Interface that we specify all the methods the EJB supports. Thus from the JNDI lookup method we get at the Home interface and from there the remote interface.

 

The code of the methods is stored in the VijayEJBClass but we do not directly instantiate this object, the EJB system does it for us. This is why we need to specify all the three classes/interfaces in the xml files. We call the only function in the EJB ourEJBfunction.

 

This returns a string s that we use to create a attribute ourattribute. We use the request and response objects that we passed as parameters. We would want to pass these values to our jsp page. To call a jsp page from our servlet we first need to create a RequestDispatcher object.

 

To do this we first use the getServletContext method in the HttpServlet class or better still from the GenericServlet class. It is a convenience method and returns a ServletContext object from the servlet's ServletConfig object. The getRequestDispatcher method in the interface ServletContext takes a string which is the path of a resource.

 

This path name must start with a / which makes it relative to the context root. The function returns a RequestDispatcher object which is nothing but a wrapper for the resource in our case the jsp file. This resource can be static or dynamic.  This interface has two methods only forward and include.

 

The include method calls the jsp file and passes it the request and response objects. The jsp file can now pick up the value of the text box called name and also the attribute ourattribute.

 

·         Create file VijayJsp.jsp in c:\ejb3\src

 

VijayJsp.jsp

<% String s1 = request.getParameter("name"); %>

<% String s2 = (String) request.getAttribute("ourattribute"); %>

Hi1 <%= s1%>, <%= s2%>

 

 

A jsp file first gets converted into a java servlet and then compiled and then executed. We use the same request object that we passed to retrieve the Parameter name, the name of the text box and the attribute ourattribute in two strings s1 and s2. We then display the two strings.

 

Finally if we look at the log file server.log we will realize that four of our methods get called.

 

doPost

setSessionContext

ejbCreate

ourEJBfunction

 

The doPost kicks it off and the first method to be called in our EJB is the setSessionContext method. We do nothing here but technically we should store the SessionContext passed to us. Then our EJB gets created by the lookup method so the ejbCreate method gets called. Finally we ourselves call our EJB method ourEJBfunction.

 


 

EJB and a Java application

 

The earlier example had the bean being called from a web page. In this example we will have it called from a Java application. A directory ejb4 is created with a sub directory src to store all the files. As before we start with the build.xml file first.

 

build.xml

<project name="cart" default="vijay" basedir=".">

<target name="vijay" >

<delete dir="../build"/>

<mkdir dir="../build"/>

<javac srcdir="." destdir="../build" classpath="C:\Sun\AppServer\lib\j2ee.jar" />

<jar destfile="../build/VijayJarContainsEJB.jar">

<zipfileset dir="../build" includes="VijayEJBClass.class,VijayRemoteInterface.class,VijayHomeInterface.class, IdVerifier.class " />

<metainf dir="." includes="ejb-jar.xml,sun-ejb-jar.xml"/>

</jar>

<jar destfile="../build/VijayJarContainsMain.jar " manifest="MANIFEST.mf">

<zipfileset dir="../build" includes="VijayClient.class" />

<metainf dir="." includes="application-client.xml,sun-application-client.xml"/>

</jar>

<jar destfile="../build/VijayFinalEar.ear" update="true">

<fileset dir="../build" includes="VijayJarContainsEJB.jar"/>

<fileset dir="../build" includes="VijayJarContainsMain.jar"/>

<metainf dir="." includes="application.xml"/>

</jar>

<exec executable="cmd.exe" >

<arg line=" /c C:/Sun/AppServer/bin/asadmin deploy --user admin --password vijay712 --host office --port 4848 --virtualservers server --force=true --name VijayAppName --upload=true ../build/VijayFinalEar.ear" />

</exec>

</target>

<target name="vijay1" >

<exec executable="cmd.exe" >

<arg line=" /c C:/Sun/AppServer/bin/asadmin undeploy --user admin --password vijay712 --host office --port 4848 VijayAppName" />

</exec>  

</target>

</project>

 

The file starts with the optional project name called cart and an optional basedir pointing to the current directory in addition to the mandatory options. The default value for the basedir attribute is always the current directory. The default target is vijay as before and viajy1 to undeploy the application. 

 

All source files are placed in the src directory wherein now there are four java files, 3 for the EJB and one for our application and the build files are placed in the build directory, which gets created every time the batch file is executed.   

 

The first jar file created is the one that contains the EJB and is aptly named VijayJarContainsEJB.jar, this file is placed in the build directory by the jar task. Then using the zipfileset task, three class files are added to the above jar file. A EJB is always structured into a minimum of three files. The first is the home file or interface VijayHomeInterface. This interface simply contains a method create. We get a reference to this object and use the create method to get a VijayRemoteInterface object or the remote object. It is this remote interface that contains the list of methods supported by the EJB. The actual code is present in a third class that we call VijayEJBClass.

 

The big guys at sun prefer this structure when coding an EJB. The xml deployment files go into the meta-inf directory. For a EJB there are two, ejb-jar.xml and sun-ejb-jar.xml.

 

sun-ejb-jar.xml

<sun-ejb-jar>

<enterprise-beans>

<unique-id>1</unique-id>

<ejb>

<ejb-name>VijayEJBName</ejb-name>

<jndi-name>VijayJNDIName</jndi-name>

</ejb>

</enterprise-beans>

</sun-ejb-jar>

 

This file starts with the root element sun-ejb-jar followed by the enterprise-beans element. The unique-id element is optional and as the name recommends gives our bean a unique number or id. Choose any number that you want to represent your EJB. The next two elements ejb-name and jndi-name create a link between the jndi name and the EJB. The ejb-name can be only one but the jndi name can be many. We will explain the link between the two when we do other types of EJB’s. The ejb-name must match the display name of the bean. The enterprise-beans will contain one ejb element for each bean the deployment file defines.

 

The unique-id element is generated by the system that creates the xml files and should be automatically updated each time the EJB is redeployed by the EJB tools. As developers, we should not change this id.

 

ejb-jar.xml

<ejb-jar>

<display-name>VijayEJBDisplayName</display-name>

<enterprise-beans>

<session>

<display-name>VijayEJBDisplayName1</display-name>

<ejb-name>VijayEJBName</ejb-name>

<home>VijayHomeInterface</home>

<remote>VijayRemoteInterface</remote>

<ejb-class>VijayEJBClass</ejb-class>

<session-type>Stateful</session-type>

<transaction-type >Container</transaction-type>

<security-identity >

<use-caller-identity/>

</security-identity>

</session>

</enterprise-beans>

<assembly-descriptor >

<container-transaction >

<method>

<ejb-name>VijayEJBName</ejb-name>

<method-intf >Remote</method-intf>

<method-name> getContents </method-name>

</method>

<trans-attribute >Required</trans-attribute>

</container-transaction>

<container-transaction>

<method>

<ejb-name>VijayEJBName</ejb-name>

<method-intf>Remote</method-intf>

<method-name>removeBook</method-name>

<method-params>

<method-param>java.lang.String</method-param>

</method-params>

</method>

<trans-attribute>NotSupported</trans-attribute>

</container-transaction>

<container-transaction>

<method>

<ejb-name>VijayEJBName</ejb-name>

<method-intf>Remote</method-intf>

<method-name>addBook</method-name>

<method-params>

<method-param>java.lang.String</method-param>

</method-params>

</method>

<trans-attribute>Required</trans-attribute>

</container-transaction>

</assembly-descriptor>

</ejb-jar>

 

This file ejb-jar.xml is where the actual contents of the EJB are defined and you rightly noticed it is much larger than the previous version. The root element is ejb-jar. The first tag is the display name which gets used by EJB tools to display the name of the EJB, it is different from its actual name.

There are two display names that are for the jar and the session. We do not get to see these names used anywhere.

 

Then comes the start of all the beans specified by the enterprise-beans element. This is really the start of the file. Our bean is a session bean and now follows all the properties of the session bean. Bear in mind that there are many more types of bean.

 

The first tag in session is the display name of the bean. Then comes the name of our bean VijayEJBName  as stated by the ejb-name element. This is followed by the home, remote and ejb-class elements as explained earlier. We have two sub session types, Stateful and Stateless and every bean has two transaction management types, Container or Bean.

 

The EJB world has security built-in. We will be executing methods present in the EJB. While doing this, should we use the callers security identity or should a special run-as identity element run-as be used. We are using the callers identity as the security identity by specifying the element use-caller-identity. The element container-transaction decides the scope of the transaction. These scopes apply when we execute a bean method. The method element specifies the start of the method and then we have the real name of the Ejb VijayEJBName to which the method belongs.

 

We need a way to distinguish a single method that has the same signature or prototype but is defined in multiple ways in the home and component interfaces. The method-intf element is used for this purpose. This element can take four values, Home , Remote, Local and LocalHome.

 

In our case, we are specifying the method getContents which represents the method in the Remote interface VijayRemoteInterface. Had we use Home then the reference would be to a similar method in our Home interface VijayHomeInterface. However, we do not have a method getContents in the Home interface. If we did and the signature was the same, we would have to have a unique of identifying which interface the method we are defining belongs to.

 

Every transaction has boundaries and the trans-attribute decides how these are handled. It can take up to six values. Transactions happen only when we invoke the bean methods. The six values it can take are NotSupported, Supports, Required, RequiresNew, Mandatory and Never. For each method we repeat all the above elements. The method removeBook takes parameters and thus there will be one method-param element for each parameter under the element method-params.

 

The method removeBook is not present in our Remote interface or EJB class but we do not get an error. Thus the deployment files are only an indication of what to expect. Also in the build file we mention the class file IdVerifier.class and even that is missing, but no error.

 

Now we build the second jar file, that contains the actual application called VijayJarContainsMain.jar. We add the manifest file ourselves called Manifest.mf instead of having the system generate it.

 

Manifest.mf

Manifest-Version: 1.0

Created-By: 1.4.0 (Sun Microsystems Inc.)

Main-Class: VijayClient

 

A manifest file in this case tells anyone that the Main-Class in the jar file will be a class called VijayClient. It is this class that contains the Main function and it should be the first to get executed.

The metainf task tells us that there are two xml files in this jar, application-client.xml and sun-application-client.xml. Lets look at them in detail.

 

application-client.xml

<application-client>

<display-name>VijayDisplayName</display-name>

<ejb-ref>

<ejb-ref-name>ejb/VijayEJBRefName</ejb-ref-name>

<ejb-ref-type>Session</ejb-ref-type>

<home>VijayHomeInterface</home>

<remote>VijayRemoteInterface</remote>

</ejb-ref>

</application-client>

 

sun-application-client.xml

<sun-application-client>

<ejb-ref>

<ejb-ref-name>ejb/VijayEJBRefName</ejb-ref-name>

<jndi-name>VijayJNDIName</jndi-name>

</ejb-ref>

</sun-application-client>

 

Both these files are necessary even though there is duplicate information somewhere else. Where the  display name is used we will come to later and the rest of the application-client file specifies the names of the home and remote interface and the type of the EJB session and its reference name. The same name VijayEJBRefName is used in the second xml file sun-application.xml to link with the jndi name VijayJNDIName.

 

Finally we create a third and final archive VijayFinalEar.ear that contains  the two jar files we have build and the main deployment descriptor application.xml.

 

application.xml

<application>

<description>Application description</description>

<display-name>VijayDisplayName1</display-name>

<module>

<java>VijayJarContainsMain.jar</java>

</module>

<module>

<ejb>VijayJarContainsEJB.jar</ejb>

</module>

</application>

 

This file will contain two module statements as we have two modules VijayJarContainsMain.jar and VijayJarContainsEJB.jar and the Display Name VijayDisplayName1.

 

The application is deployed using the asadmin command and it is called VijayAppName using the name option. On deployment, a subdirectory called VijayAppName, the name of our application gets created under C:\sun\appserver\domains\domain1\applications\j2ee-apps. The following files also get copied into this directory as shown above.

 

Directory of C:\Sun\AppServer\domains\domain1\applications\j2ee-apps\VijayAppName

<DIR>          META-INF

<DIR>          VijayJarContainsEJB_jar

<DIR>          VijayJarContainsMain_jar

VijayAppNameClient.jar

 

Directory of C:\Sun\AppServer\domains\domain1\applications\j2ee-apps\VijayAppName\META-INF

MANIFEST.MF

application.xml

sun-application.xml

 

Directory of C:\Sun\AppServer\domains\domain1\applications\j2ee-apps\VijayAppName\VijayJarContainsEJB_jar

VijayHomeInterface.class

VijayRemoteInterface.class

VijayEJBClass.class

<DIR>          META-INF

 

Directory of C:\Sun\AppServer\domains\domain1\applications\j2ee-apps\VijayAppName\VijayJarContainsEJB_jar\META-INF

1,378 ejb-jar.xml

107 MANIFEST.MF

930 sun-ejb-jar.xml

 

Directory of C:\Sun\AppServer\domains\domain1\applications\j2ee-apps\VijayAppName\VijayJarContainsMain_jar

1,706 VijayClient.class

<DIR>          META-INF

 

Directory of C:\Sun\AppServer\domains\domain1\applications\j2ee-apps\VijayAppName\VijayJarContainsMain_jar\META-INF

275 application-client.xml

125 MANIFEST.MF

434 sun-application-client.xml

 

To start with in the VijayAppName directory we get three subdirectories and a jar file called Client.jar with the application name VijayAppName prefaced. Then the two jar files have they own sub-directories.

 

The Meta-Inf directory simply has the Manifest and the two application files application.xml and sun-application.xml. The EJB jar file directory has the three class files and the meta-inf directory has the two xml files and the system generated manifest.mf file. Ditto for the main Client file.

 

To run the client we use the batch file appclient.

 

>appclient -client c:\sun\appserver\domains\domain1\applications\j2ee-apps\VijayAppName\VijayAppNameClient.jar

 

This gives us the following output.

 

Vijay

Sonal

Mukhi

 

The appclient batch file calls Java to run the  Main function in the jar file specified by the client parameter.

 

Now lets look at the client file that will talk to our EJB.

 

VijayClient.java

import java.util.*;

import javax.naming.Context;

import javax.naming.InitialContext;

import javax.rmi.PortableRemoteObject;

public class VijayClient

{

public static void main(String[] args)

{

try

{

Context m_Context = new InitialContext();

Object m_Object = m_Context.lookup("java:comp/env/ejb/VijayEJBRefName");

VijayHomeInterface m_VijayHomeInterface = (VijayHomeInterface)PortableRemoteObject.narrow(m_Object,VijayHomeInterface.class);

VijayRemoteInterface m_VijayRemoteInterface = m_VijayHomeInterface.create("Sonal Mukhi");

m_VijayRemoteInterface.addBook("Vijay");

m_VijayRemoteInterface.addBook("Sonal");

m_VijayRemoteInterface.addBook("Mukhi");

Vector m_Vector = new Vector();

m_Vector = m_VijayRemoteInterface.getContents();

Enumeration m_Enumeration = m_Vector.elements();

while (m_Enumeration.hasMoreElements())

{

String title = (String) m_Enumeration.nextElement();

System.out.println(title);

}

System.exit(0);

} catch (Exception ex)

{

System.err.println("Caught an unexpected exception!");

ex.printStackTrace();

System.exit(1);

}

}

}

 

The name of the java file is VijayClient.java and as it is a application, it contains the main function. There is not much difference between a servlet calling a EJB and a java application. We start by creating a InitialContext or a Context class. The InitialContext class is derived from the Context interface.

 

We use the same lookup method where we specify the name of the EJB ref name VijayEJBRefName and not the JNDI name. This is how an EJB is identified, with its ref name. The element ejb-ref is present in two xml files, application-client and sun-application-client.

 

The JNDI name is used for later references to the EJB. The lookup method returns an Object of type VijayHomeInterface and the narrow method is used to confirm the typecast. Using the home interface we need to get at the Remote interface and we use the create function to do this.

 

VijayHomeInterface.java

import java.rmi.RemoteException;

import javax.ejb.CreateException;

import javax.ejb.EJBHome;

public interface VijayHomeInterface extends EJBHome

{

VijayRemoteInterface create(String i) throws RemoteException,CreateException;

VijayRemoteInterface create() throws RemoteException,CreateException;

}

 

This time the create method takes one string as a parameter and throws two exceptions. This is why when we call this create function we have to pass a string as a parameter. It is this create that returns a Remote interface. The interface has both create methods, one with a parameter and the other without one. But first lets look at the VijayEJBClass file.

 

VijayEJBClass.java

import java.util.*;

import javax.ejb.*;

public class VijayEJBClass implements SessionBean

{

Vector m_Vector;

public void ejbCreate() throws CreateException

{

m_Vector = new Vector();

System.out.println("ejbCreate void");

}

public void ejbCreate(String s) throws CreateException  {

System.out.println("ejbCreate one" + s);

m_Vector = new Vector();

}

public void addBook(String title)

{

System.out.println("addBook");

m_Vector.addElement(title);

}

public Vector getContents()   {

return m_Vector;

}

public void ejbRemove() {}

public void ejbActivate() {}

public void ejbPassivate() {}

public void setSessionContext(SessionContext sc)    {

}

}

 

The ejbCreate function earlier had no parameters but now has one string. The value of this string is what we have passed in the Client program,  Sonal Mukhi. Thus we have two ejbCreate methods.

 

The file ejb-jar.xml contains a element session-type. If its value is Stateful, then the ejbCreate method gets called with one parameter. Change it to Stateless, and the ejbCreate method with no parameters gets called. The file server.log is where the output gets written to.

 

VijayRemoteInterface.java

import java.util.*;

import javax.ejb.EJBObject;

import java.rmi.RemoteException;

public interface VijayRemoteInterface extends EJBObject   {

public void addBook(String title) throws RemoteException;

public Vector getContents() throws RemoteException;

}

 

The remote interface defines two methods, whereas the ejb-jar.xml specified three. We are calling the addBook method thrice and passing it a string. This will call the addBook method in the VijayEJBClass.java file. But before that in our ejbCreate method that get called once, we create a object that is of type Vector. A Vector object stores other objects for us. In the addBook implementation we simply call the addElement method of Vector to store the string passed. This is why we use the Vector class, it knows how to handle multiple objects. Coming back to our client, we create a new Vector object and call the second method in our EJB getContents that simply returns the Vector object created in the EJB. Now back to our client file.

 

We use the elements method of a vector class to give us a list of the objects in the Vector as an enumeration. A while loop is put use to iterate over all the objects. The hasMoreElements  method of the enumeration returns a true if there are more elements, false if there are none.  The nextElement method returns to us the active element which we print out. Thus we are using the bean to store all the objects for us in a Vector object.

 

When we run the asadmin command as

 

VijayAppName <j2ee-application>

adminapp <web-module>

Command list-components executed successfully.

 

This shows is that our application VijayAppName has been successfully registered with the system. We then start the Admin console by clicking on the menu option. We login in as admin and our password vijay712. We then click on the key in front of the words application in the left pane.  Then again on the key in front of Enterprise Applications where we will see our application VijayAppName. Then we will click on our applications and see its location matches ours and that it is enabled. Scrolling down its tells us that we have two sub-components VijayDisplayName of type AppClientModule and VijayJarContainsEJB.jar of type EJBModule. Thus our application got successfully registered with asadmin.