Home > Apache Geronimo v1.1 > Documentation > Apache Geronimo v1.1 - User's Guide > Sample applications > Very simple Session EJB example |
This is an example of a JSP-page calling a Session Bean. The result looks like this:
First of all, I know I can't spell. I hope you can understand it anyway.
In this example, I have not used any of the things making EJB development easier, like Eclipse, Ant or Xdoclet. I have tried to strip of everything just to make this example as easy as possible to understand. (On the other hand, some of the things in all the deployment descriptors I don't understand myself, so perhaps it would have been possible to strip of even more.) This is an example using geronimo 1.1, java 1.4 and EJB 2.1.
OK, what I want is an EJB that can tell the time, this is what the code for the bean looks like:
package mytimepak; public class MyTimeBean implements javax.ejb.SessionBean { public void ejbCreate() {} public void ejbActivate() {} public void ejbPassivate() {} public void setSessionContext(javax.ejb.SessionContext ctx) {} public void unsetSessionContext() {} public void ejbRemove() {} public String getTime() { String s = new java.util.Date().toString(); return s; } }
I have put my EJB in a package that I call mytimepak. The first 6 methods are implementations of the javax.ejb.SessionBean interface. I don't realy know what good they are, but the ejbCreate method is like a constructor, in this case an empty constructor. To enable clients to call the EJB one must provide so called home and remote interfaces. The first one, home interface, is used to locate the EJB, the other, the remote interface is used to invoke methods on the EJB. The remote interface is just like a normal interface used for RMI. As this EJB will only be used from a JSP-page that is run in the same server (same JVM) I use another type of interfaces that don't make use of the network. Thay are called LocalHome and Local interface instead, but they are just the same.
This is the code for the local home interface (coresponding to the home interface):
package mytimepak; public interface MyTimeLocalHome extends javax.ejb.EJBLocalHome { public MyTimeLocal create() throws javax.ejb.CreateException; }
As you can see, the LocalHome interface is like a constructor returning MyTimeLocal.
This is the code for the Local interface (coresponding to the remote interface)
package mytimepak; public interface MyTimeLocal extends javax.ejb.EJBLocalObject { public java.lang.String getTime() ; }
This is just a plain interface for the methods (I refuse to call them business methods) of the EJB, in this case the getTime() method.
This code is not hard to understand, the tricky part is the deployment descriptors. You need 2 of them, ejb-jar.xml and openejb-jar.xml. This is what they look like:
ejb-jar.xml:
<?xml version="1.0" encoding="UTF-8"?> <ejb-jar id="ejb-jar_1" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd" version="2.1"> <description>Example of a session bean</description> <display-name>MyTimeBeanEJBName</display-name> <enterprise-beans> <session id="Session_MyTime"> <description>An EJB named MyTimeBean</description> <display-name>MyTimeBeanName</display-name> <ejb-name>MyTimeBean</ejb-name> <local-home>mytimepak.MyTimeLocalHome</local-home> <local>mytimepak.MyTimeLocal</local> <ejb-class>mytimepak.MyTimeBean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> </session> </enterprise-beans> </ejb-jar>
First you have some stuff that I don't know what good they are, for example the display-name tag. You can write what ever there I guess. It seams totaly unnessesary to me, but I have not tested what happens if I take it away. What does matter is the tags ejb-name, local-home, local, ejb-class, session-type. I think that the ejb-name is part of what clients use to locate the bean (see the next xml-file). The local-home, local and ejb-class tags points out the classes. Session type tells if it is a statefull or stateless session bean. Transaction type doen't make sence I think, there is no transactions in a session bean, is there? I have tryed to read more about this deployment descriptors here: http://cwiki.apache.org/GMOxDOC11/deployment-plans.html But I have not bean able to figure it out. On the other hand, I don't think ejb-jar.xml files are needed in the next generation EJB 3.0 so perhaps one should not spend to much time trying to understand it.
Here comes the openejb-jar.xml file:
<?xml version="1.0" encoding="UTF-8"?> <openejb-jar xmlns="http://www.openejb.org/xml/ns/openejb-jar-2.1" xmlns:nam="http://geronimo.apache.org/xml/ns/naming-1.1" xmlns:pkgen="http://www.openejb.org/xml/ns/pkgen-2.0" xmlns:sec="http://geronimo.apache.org/xml/ns/security-1.1" xmlns:sys="http://geronimo.apache.org/xml/ns/deployment-1.1"> <sys:environment> <sys:moduleId> <sys:groupId>default</sys:groupId> <sys:artifactId>TimeBean_artifact_in_openejb</sys:artifactId> <sys:version>1.0</sys:version> <sys:type>car</sys:type> </sys:moduleId> </sys:environment> <enterprise-beans> <session> <ejb-name>MyTimeBean</ejb-name> <ejb-ref> <ref-name>ejb/MyTimeBean</ref-name> <ejb-link>MyTimeBean</ejb-link> </ejb-ref> </session> </enterprise-beans> </openejb-jar>
Again, I don't understand much of the first part, but the ejb-ref part is important. ref-name=ejb/MyTimeBean is what clients use to lookup the EJB. ejb-link=MyTimeBean is the same as ejb-name=MyTimeBean in ejb-jar.xml. The ejb-link and the ejb-name must be the same.
(Why this information has bean split up in 2 files is a mystery to me. I guess that it has something to do with what is generic for the application (ejb-jar.xml) and other stuff that is spesific to the application server. If you were to deploy this application on another application server than geronimo you would only have to change the openejb-jar file. That sounds good. On the other hand, now you have the extra complexity of knowing what goes where and that sure aint easier than having to rewrite one sigle deployment descriptor if you would face the rare situation of deploying your code on another type of server. But this is ofcourse my personal oppinion based on limited understanding of EJB.)
Allright, now for the JSP client of this EJB.
This is my JSP-page, I comment the code directly in the code:
<%@ page contentType="text/html" import="mytimepak.*, javax.naming.* " %> <html<head><title>Time</title></head><body> <% String s="-"; // Just declare a string try { // This creates a context, it can be used to lookup EJBs. Using normal RMI you would // have to know port number and stuff. The InitialContext holds info like // server names, ports and stuff I guess. Context context = new InitialContext(); // MyTimeLocalHome is a rference to the EJB MyTimeLocalHome myTimeHomeLocal = (MyTimeLocalHome)context.lookup("java:comp/env/ejb/MyTimeBean"); // java:comp:env is like a reference to the local server. ejb is where you find the EJBs and // MyTimeBean is the name of the EJB that is written in the deployment descriptors. // This is like a constructor returning a MyTimeLocal, an interface for the EJB on which you // can call methods (but not access variables as with any interface) MyTimeLocal myTimeLocal = myTimeHomeLocal.create(); // So, just go ahead and call a method (in this case the only method). s = myTimeLocal.getTime(); } catch (Exception e) { s=e.toString(); } %> This is the time returned from the EJB: <%=s%>
Note that this JSP-page also have to include mytimepak.MyTimeLocalHome and mytimepak.MyTimeLocal. I have done that in the first line import="mytimepak.*. This two class files must be in the WEB-INF/classes directory, and not only that, in the WEB-INF/classes/mytimepak directory. I'll get back to that later.
If the JSP is simple, again the deployment descriptors are hard (or at least that is what I think). This is what they look like:
geronimo-web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://geronimo.apache.org/xml/ns/j2ee/web-1.1" xmlns:nam="http://geronimo.apache.org/xml/ns/naming-1.1" xmlns:sec="http://geronimo.apache.org/xml/ns/security-1.1" xmlns:sys="http://geronimo.apache.org/xml/ns/deployment-1.1"> <sys:environment> <sys:moduleId> <sys:groupId>default</sys:groupId> <sys:artifactId>MyTimeWeb</sys:artifactId> <sys:version>1.0</sys:version> <sys:type>car</sys:type> </sys:moduleId> </sys:environment> <context-root>/mytime</context-root> </web-app>
Only inportant here (as far as I understand) is the context-root that becomes the URI.
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name> MyTImeWeb</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!-- To refer local EJB's --> <ejb-local-ref> <ejb-ref-name>ejb/MyTimeBean</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <local-home>mytimepak.MyTimeLocalHome</local-home> <local>mytimepak.MyTimeLocal</local> <ejb-link>MyTimeBean</ejb-link> </ejb-local-ref> </web-app>
welcome-file is what file the server will show if no file name is in the URI. ejb-local-ref is important. Here you must get the data right in all the tags (or so I think). Note how ejb/MyTimeBean corresponds to ref-name int openejb-jar. ejb-link seams to be unessesary, but we can't deply it without the local-home and local tags pointing out the classes for the interfaces.
There are 2 more deployment descriptors, the ones for the application as a whole. This is what they look like:
application.xml
<?xml version="1.0" encoding="UTF-8"?> <application id="Application_ID" version="1.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/application_1_4.xsd"> <display-name>MyTimeEJBApp</display-name> <module> <ejb>mytime-ejb.jar</ejb> </module> <module id="WebModule_1158667626422"> <web> <web-uri>mytime-web.war</web-uri> <context-root>/mytime</context-root> </web> </module> </application>
This file tell which two modules, the ejb and the web, that the ear file consists of.
geronimo-application.xml:
<?xml version="1.0" encoding="UTF-8"?> <application xmlns="http://geronimo.apache.org/xml/ns/j2ee/application-1.1" xmlns:sec="http://geronimo.apache.org/xml/ns/security-1.1" xmlns:sys="http://geronimo.apache.org/xml/ns/deployment-1.1" application-name="timebeanexmaple"> <sys:environment> <sys:moduleId> <sys:groupId>default</sys:groupId> <sys:artifactId>MyTime_ArtifactId</sys:artifactId> <sys:version>1.0</sys:version> <sys:type>car</sys:type> </sys:moduleId> </sys:environment> </application>
This file is pretty empty. The only thing of importance is the artifactId, it shows if you look a the lists of modules at http://localhost:8080/console/portal/apps/apps_ear like this: default/MyPhonebook_ArtifactId/1.0/car
(Anybody knows what "car" stands for?)
Allright, this is how I have organised my files :
app-home (in my case c:\java\j2ee\mytime) ¦ ¦ +---ejb ¦ +---META-INF ¦ ¦ ejb-jar.xml ¦ ¦ openejb-jar.xml ¦ ¦ ¦ +---mytimepak ¦ MyTimeBean.java ¦ MyTimeLocal.java ¦ MyTimeLocalHome.java ¦ +---META-INF ¦ application.xml ¦ geronimo-application.xml ¦ +---web ¦ index.jsp ¦ +---WEB-INF ¦ geronimo-web.xml ¦ web.xml ¦ +---classes +---mytimepak MyTimeLocal.class this file must be copyed here MyTimeLocalHome.class this file must be copyed here
First we must compile the EJB.
Then copy the interface classes to the WEB-INF\classes\mytimepak directory
Last task is to pack the EJB-jar file the WEB-war file and finaly the EAR-file.
To compile the EJB we must have geronimo-ejb_2.1_spec-1.0.1.jar in the classpath. I have installed geronimo in C:\geronimo\geronimo-1.1 and This is a bat-script that does this. Modern programmers use ant ofcourse, but for clarity, this is exactly what is needed to get the ear. So beginning in the app-home directory (in my case cd c:\java\j2ee\mytime)
set CLPATH=C:\geronimo\geronimo-1.1\repository\org\apache\geronimo\specs\geronimo-ejb_2.1_spec\1.0.1\geronimo-ejb_2.1_spec-1.0.1.jar cd ejb javac -classpath %CLPATH% mytimepak\*.java copy mytimepak\MyTimeLocal.class ..\web\WEB-INF\classes\mytimepak\ copy mytimepak\MyTimeLocalHome.class ..\web\WEB-INF\classes\mytimepak\ jar -cf ../mytime-ejb.jar mytimepak META-INF cd ..\web jar -cf ../mytime-web.war index.jsp WEB-INF cd .. jar -cf mytime-ear.ear mytime-ejb.jar mytime-web.war META-INF
As you can see here I start with declaring a variable CLPATH just not to get to long lines. Then cd to the ejb-directory and compile the EJB. Then copy the interface flasses to the web application. Then I use jar to first create an jar file for the EJB, then a war-file for the web, and finaly put this to jars (or mor corectly the jar and the war) in an ear file. Note how the META-INF/WEB-INF is directly in the jar-file root directory for all the jars.
Finaly the ear must be deployed. I use the geronimo deploy tool like this:
c:\geronimo\geronimo-1.1\bin\deploy.bat --user system --password manager deploy mytime-ear.ear
(If you change anything, remake the jar, war and ear, and change to redeploy mytime-ear.ear.
Also, you need to change the path to deploy.bat if you have not installed geronimo 1.1 in the
directory c:\geronimo\geronimo-1.1)
Now we can look at the application at http://localhost:8080/mytime
The whole source code can be downloaded in this zip-file: mytime.zip
Next: the CMP EJB phonebook example.
--------------------------------------------------------------------------------
By Mattias Malmgren [email protected]
2006-09-28