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.
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
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.
<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.
<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.
<% 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.
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-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.
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.
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.
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.
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.